4

I have a data file that looks as follows

ALGO = Fast
EDIFF = 6.7e-05
ENCUT = 520
IBRION = 2
ICHARG = 1
ISIF = 2
ISMEAR = 0
ISPIN = 2

I'm interested in copying the first column of uneven width but where every element ends with an = character.

The kill-yank rectangle mode does not work in this case, since the columns are of uneven width.

rambalachandran
  • 245
  • 1
  • 12

3 Answers3

5

You can also achieve this using shell-command-on-region and the Unix utility cut.

  1. Highlight the region.
  2. Press C-u M-|, enter "cut -d= -f1", press RET. The prefix argument C-u tells shell-command-on-region to replace the region with results. If you don't want to (or can't) modify the current buffer, you can still use M-| (now without the prefix argument). In this case its output will be in the buffer named *Shell Command Output*.

    The command "cut -d= -f1" selects the first field (-f1) after splitting lines using "=" as the delimiter (-d=) (see "man cut" for details).

  3. Press C-x C-x to exchange point and mark, i.e. highlight results.
  4. M-w to copy into the kill ring.
  5. Press C-/ to undo the changes to get back the text after "=".

Now you have the text before equal signs is in the kill ring.

Edit:

Alternatively you can align this text so that kill-rectangle works:

  1. Highlight the region.
  2. Do M-x align-regexp, enter "=", press RET.
  3. Highlight the rectangle containing the first column.
  4. Press C-x r M-w to invoke copy-rectangle-as-kill.
  5. Press C-/ to return the region to its original state.

Now you can yank the first column with C-x r y.

Constantine
  • 9,072
  • 1
  • 34
  • 49
  • The edit part worked great. In the first answer, how can I copy the data from minibuffer. Pressing `C-x C-x` closes the minibuffer. Also can you please explain how the command " `cut -d= -f1` " works. – rambalachandran Jan 30 '16 at 14:18
  • @WanderingMind: If I understand correctly you want to copy results of the `cut` command shown in the echo-area after you press `M-|`. Since these results are *not* in the mini-buffer (but are shown in the same area of the screen, called "echo area" in Emacs-speak), you cannot copy from there: you need to switch to the buffer called `*Shell Command Output*` and copy it *from there.* As for the `cut -d= -f1` command: please see the updated answer and the manual page for the `cut` utility (there is a link near the top of the answer, but you can also do `M-x man`, type `cut`, and press return). – Constantine Feb 01 '16 at 16:08
2

It feels as if the combination of the multiple-cursors and iy-go-to-char packages is to do exactly this kind of stuff. First of all, if you don't have those packages installed, they can be installed via MELPA.

  1. Select all the lines you need and then do M-x mc/edit-lines (or the default binding C-S-c C-S-c).
  2. Move all the cursors to the beginning of the lines (C-a) as that's from where we want to start the selection. /If you are using multiple-cursors of the first time, or hitting C-a for the first time while this mode is active, you will be asked if you want to run that command for each cursor, and you will need to say yes./
  3. Set mark (C-SPC) and do M-x iy-go-to-char (It's good to have these commands bound to your favorite keys for efficiency) and hit =. You should now be seeing an effectively "non-rectangle" area having been selected by all those cursors.
  4. Adjust the selection using C-b.
  5. Now do a plain copy M-w. The magic is that doing a plain old kill/copy in multiple-cursors mode kills the whole collective selection as a rectangle.
  6. Quit the multiple-cursors mode by hitting RET.
  7. You can now paste that rectangle anywhere using M-x yank-rectangle or C-x r y.

If all those sounds too cumbersome, it's not. Proof is in the GIF below :)

enter image description here

Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
1

There are many ways to do this.

1. Using built-in emacs stuff

  1. Switch to a programming mode where aligning at the equal sign makes sense, e.g.: M-x python-mode

  2. Place point right at the end of the line with the last =.

  3. Call M-x align.

  4. Call copy-rectangle-as-kill on the rectangle before the =.

  5. Undo the alignment with C-x u

2. Using the versatile matches function

The following function matches is quite usefull for collecting strings matching some regular expression. Please also read the doc-string of that function.

  1. Install the function in your init files. (You only need this once to use it later on.)
  2. Select the region with the strings you want to collect. If region is not active then the region from (point-min) to (point-max) is used.
  3. Input M-x matches RET. You can also bind matches to some key if you like.
  4. Input your regular expression. In your case that can be ^[^= ]+. Press two times RET. (The first time you confirm the default 0 for the number of the sub-expression you want to collect from the match. Thereby 0 means the full match.)
  5. Now you can yank a string with all matches where you want to have it.

I've added the protection against matching empty strings before pasting the function here. Hopefully, that did not break anything. Just leave a comment if you encounter problems. I will fix them.

(defcustom matches-default-separator " "
  "Default separator string for `matches'."
  :group 'matching
  :type 'string)

(defvar matches-separator-history nil
  "History of the separators for `matches'.")

(defun matches (re &optional n b e unique)
  "Return a list of all occurences of re within region from B to E.
If B and E are `nil' then search from point to end of buffer.
If called interactively the list goes to the kill-ring.
If UNIQUE is non-nil or if `matches' is called with interactively with prefix arg
then collect each matching string only once.
On interactive calls also the separator is asked for."
  (interactive "sRegular expression:\nMNumber of subexpression (default: 0):")
  (let (sep)
    (if (stringp n) (setq n (string-to-number n)))
    (when (called-interactively-p 'any)
      (setq unique current-prefix-arg)
      (setq sep (read-string (format "Separator (default: \"%s\"):" matches-default-separator) nil 'matches-separator-history matches-default-separator))
      (if (use-region-p)
      (progn
        (setq b (region-beginning))
        (setq e (region-end)))))
    (let (ret)
      (save-excursion
    (unless n (setq n 0))
    (unless b (setq b (point)))
    (unless e (setq e (point-max)))
    (goto-char b)
    (condition-case err
        (while (search-forward-regexp re e 'noErr)
          (let ((str (match-string-no-properties n)))
        (unless (and unique
                 (assoc-string str ret))
          (setq ret (cons str ret)))
        (if (= (length str) 0) ;; protection against inappropriate re
            (forward-char))))
      (end-of-buffer))
    (setq ret (nreverse ret))
    (if (called-interactively-p 'any)
        (progn
          (message "Matches found: %s" ret)
          (kill-new (mapconcat 'identity ret sep))))
    ret))))
Tobias
  • 32,569
  • 1
  • 34
  • 75