2

The following works in a separate program terminal window (to copy multiple specified files in the same directory), but the command does not work in Eshell (No such file or directory). Is there an alternative way of accomplishing this task using Eshell?

cp /Users/HOME/.0.data/.0.emacs/.0.sources/emacs/\
{buffer.c,\
buffer.h,\
dispextern.h,\
keyboard.c,\
nsterm.m,\
w32term.c,\
window.h,\
xdisp.c,\
xterm.c} \
/Users/HOME/Desktop/emacs/src/

Background:  What did I try? I blocked and copied the above text and pasted/yanked it at the command prompt in an *eshell* buffer, and then I pressed the enter/return key. What happened? I received an error message: No such file or directory.

  • You can use lisp forms such as directory-files, e.g., ls (directory-files "~" t "\\..*") for an example. The doc claims that the globbing syntax is similar to zshell. But, it seems that this does not include your expression. – Tobias May 23 '16 at 9:17
  • @Tobias -- thank you for the suggestion to use an elisp form to create the list of files to be copied. I posted a draft answer which can be accepted in about 48 hours. I'd be happy to give you credit for answering the question if you would like to post a variation, or a different solution if anything else comes to mind. – lawlist May 23 '16 at 16:48
  • Now I have learned that there is actually much more. If eshell reckognizes a file name pattern the or-operator works. Try: ls ~/.*(bash|profile)* The leading ~/. or anything that makes clear that you mean a file name pattern and not a lisp form is important. So your example should read cp /Users/HOME/.0.data/.0.emacs/.0.sources/emacs/(buffer.c|buffer.h|dispextern.h|keyboard.c|nsterm.m|w32term.c|window.h|xdisp.c|xterm.c) ~/Desktop/emacs/src/ – Tobias May 23 '16 at 17:35
  • @Tobias -- that doesn't seem to work with the default eshell settings: Debugger entered--Lisp error: (error "Unknown predicate character ‘b’") signal(error ("Unknown predicate character ‘b’")) error("Unknown predicate character `%c'" 98) eshell-parse-modifiers() . . . – lawlist May 23 '16 at 18:04
  • Ah yes. You have to add something at the end of the group expression (...) else this becomes a predicate. E.g., add an empty string '' such that the expression beomes (...)''. Sorry. This happend because your example requires some blind-walking;-). This is also the reason why I posted this as a comment and not as a solution. My simple ls-example worked because of the trailing star *. – Tobias May 23 '16 at 18:38
2

Eshell accepts ksh-like syntax. Amog others you can specify a group of files by (file1|file2|...|fileN). But, that is a bit tricky since parentheses also indicate lisp forms to be evaluated (for an example see the answer by lawlist).

Furthermore, there are also predicates in parenthesis. The predicates come as last thing of a path specification. Example:

echo *(/)

prints a list of directories.

To differentiate groups from lisp forms and predicates you must append something to the group string. Maybe, you want to append something anyway. Example:

echo (file1|file2).txt

As a last resort you can just append an empty string '':

echo (file1|file2)''

The solution to the original-posters problem would be:

cp /Users/HOME/.0.data/.0.emacs/.0.sources/emacs/(buffer.c|buffer.h|dispextern.h|ke‌​‌​yboard.c|nsterm.m|w32term.c|window.h|xdisp.c|xterm.c)'' ~/Desktop/emacs/src/

In the following I cite the help strings eshell-predicate-help-string and eshell-modifier-help-string as an add-on to the answer. Maybe the modifiers and predicates are not so well known even if they are a quite powerful system. (At least I did not know about them until I answered this question.)

Eshell predicate quick reference:

  -  follow symbolic references for predicates after the `-'
  ^  invert sense of predicates after the `^'

FILE TYPE:
  /  directories              s  sockets
  .  regular files            p  named pipes
  *  executable (files only)  @  symbolic links

  %x  file type == `x' (as by ls -l; so `c' = char device, etc.)

PERMISSION BITS (for owner/group/world):
  r/A/R  readable    s  setuid
  w/I/W  writable    S  setgid
  x/E/X  executable  t  sticky bit

OWNERSHIP:
  U               owned by effective uid
  u(UID|'user')   owned by UID/user
  g(GID|'group')  owned by GID/group

FILE ATTRIBUTES:
  l[+-]N                 +/-/= N links
  a[Mwhms][+-](N|'FILE') access time +/-/= N months/weeks/hours/mins/secs
             (days if unspecified) if FILE specified,
             use as comparison basis; so a+'file.c'
             shows files accessed before file.c was
             last accessed
  m[Mwhms][+-](N|'FILE') modification time...
  c[Mwhms][+-](N|'FILE') change time...
  L[kmp][+-]N            file size +/-/= N Kb/Mb/blocks

EXAMPLES:
  *(^@)         all non-dot files which are not symlinks
  .#*(^@)       all files which are not symbolic links
  **/.#*(*)     all executable files, searched recursively
  ***/*~f*(-/)  recursively (though not traversing symlinks),
        find all directories (or symlinks referring to
        directories) whose names do not begin with f.
  e*(*Lk+50)    executables 50k or larger beginning with 'e'

Eshell modifier quick reference:

FOR SINGLE ARGUMENTS, or each argument of a list of strings:
  E  evaluate again
  L  lowercase
  U  uppercase
  C  capitalize
  h  dirname
  t  basename
  e  file extension
  r  strip file extension
  q  escape special characters

  S       split string at any whitespace character
  S/PAT/  split string at each occurrence of PAT

FOR LISTS OF ARGUMENTS:
  o  sort alphabetically
  O  reverse sort alphabetically
  u  uniq list (typically used after :o or :O)
  R  reverse list

  j       join list members, separated by a space
  j/PAT/  join list members, separated by PAT
  i/PAT/  exclude all members not matching PAT
  x/PAT/  exclude all members matching PAT

  s/pat/match/  substitute PAT with MATCH
  g/pat/match/  substitute PAT with MATCH for all occurrences

EXAMPLES:
  *.c(:o)  sorted list of .c files
0

Here is a draft solution based on the helpful comment of @Tobias, which suggests the possibility of using an elisp form to create the list of files to be copied. [I added the -p argument to preserve the filestamps of the files being copied.]

I may decide to either turn this into a saved function with arguments for the root directory and a list for the files within that directory; or, I may decide to create a new special eshell function named something like eshell/cp which can do regular copy stuff or something special if it finds a familiar form of arguments. [I can't accept my own answer until about 36 hours after posting a question and it's only been about 12 hours so far, so I'll check back.]

cp -p
  (let* (
      result
      (basename (file-name-as-directory "/Users/HOME/.0.data/.0.emacs/.0.sources/emacs/"))
      (files '(buffer.c buffer.h dispextern.h keyboard.c nsterm.m w32term.c window.h xdisp.c xterm.c)) )
    (dolist (file files)
      (push (concat basename (format "%s" file)) result))
    result) /Users/HOME/Desktop/emacs/src/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.