5

So in bash, When I do

echo \*
*

This seems right, as * is escaped and taken literally.

But I can't understand that, when I do

echo \\*
\*

I thought the first backslash escaped the second one thus two backslash "\\" will give me one "\" in literal. and * followed carrying its special meaning. I was expecting:

echo \\*
\file1 file2 file3

ANSWER SUMMARY: Since \ is taken literally, echo \* will behave just as echo a*, which will find any file that starts with literal "a".

Follow up question, If I want to print out exactly like

\file1 file2 file3

What command should I use? e.g. like the following but I want no space

echo \\ * 
\ file1 file2 file3
6

If you don't have a file in the current directory whose name starts with a backslash, this is expected behaviour. Bash expands * to match any existing file names, but:

If the pattern does not match any existing filenames or pathnames, the pattern string shall be left unchanged.

Because there was no filename starting with \, the pattern was left as-is and echo is given the argument \*. This behaviour is often confusing, and some other shells, such as zsh, do not have it. You can change it in Bash using shopt -o failglob, which will then give an error as zsh does and help you diagnose the problem instead of misbehaving.

The * and ? pattern characters can appear anywhere in the word, and characters before and after are matched literally. That is why echo \\* and echo \\ * give such different output: the first matches anything that starts with \ (and fails) and the second outputs a \, and then all filenames.


The most straightforward way of getting the output you want safely is probably to use printf:

printf '\\'; printf "%s " *

echo * is unsafe in the case of unusual filenames with - or \ in them in any case.

  • Is there any shells except zsh print the error message if matching fail by default? – cuonglm Mar 11 '15 at 7:38
  • fish, at least. Maybe others, although I don't recall off-hand. – Michael Homer Mar 11 '15 at 7:57
  • @cuonglm It comes from csh and was adopted by zsh and fish. – Gilles Mar 11 '15 at 22:24
  • (1) The question says “like … echo \\ * … but I want no space”.  Your answer fails because it doesn’t include the newline at the end.  (Of course you can fix that by adding ; echo or ; printf '\n'.)  (2) Why do you use single quotes for one string and double quotes for the other? – Scott Aug 28 '18 at 4:00
1

Try this:

printf "\\%s" "$(echo *)"

Explanation:

  • printf takes a format argument and zero or more arguments that are substituted into the format string
  • \\ in the format has the same meaning as in echo \\
  • %s means take the next argument and substitute it into the result
  • "$(echo *)"means: execute echo *, and put the result into a single argument to printf; it has to be put into a single argument because of how printf works
  • (1) The question says “like … echo \\ * … but I want no space”.  Your answer fails because it doesn’t include the newline at the end.  … … … … … …  (Of course you can fix that by saying printf "\\%s\n" "$(echo *)".) … … …  (2) You could change \\ to \ in your answer (printf "\%s" "$(echo *)"), and it would work as well.  … … … …  … … …  … …  (3) Also, as far as I can tell, echo "\\$(echo *)" would work as well.  … (Cont’d) – Scott Aug 28 '18 at 4:08
  • (Cont’d) …  (4) The real problem with this answer is that it depends on the mercurial behavior of echo. If there is a file named -e or -n (and it is the first file in the * list), it will disappear. – Scott Aug 28 '18 at 4:10
1

Try...

set file*
printf %s\  "\\$@"

It will prepend a a backslash to the head of the array - so only the first element. You can do the same with the end. You can get get them all split out on \\ backslashes like:

printf \\%s file*

...or...

set file*; IFS=\\; printf %s\\n "$*"
  • (1) The question says “like … echo \\ * … but I want no space”. Your answer fails because it doesn’t include the newline at the end. (It seems especially odd that you missed this, since you included a trailing newline in the last line of your answer, where you’re answering a different question.) (2) Since your answer uses set, it should be put into a subshell, to avoid setting / changing the positional parameters in an outer scope / context. … (Cont’d) – Scott Aug 28 '18 at 4:10
  • (Cont’d) …  (3) The question talks about echo … *, and indicates that the current directory contains file1, file2, and file3. (Arguably, we should treat these as placeholders rather than examples.) Using file* in your answer seems inappropriate. (4) So, any correct answer should include a plain *. Making the appropriate change to your answer, we get set *, which can fail if there is a file whose name begins with - (dash). … (Cont’d) – Scott Aug 28 '18 at 4:10
  • (Cont’d) …  (5) Why use $@ and a literal space in the format string when you could use $* and let the shell interpolate space(s) for you? (OK, yes, I know about IFS.) (6) People less clever and tricky than you might not realize that you are using a literal space in the format string because it isn’t quoted. A good answer would make a detail like that explicit, and explain it. – Scott Aug 28 '18 at 4:10
  • @Scott - actually, literal space is between the lines. the space character in the format string is quoted, though. – mikeserv Aug 28 '18 at 11:58
1

No, bash escape character preserves the literal value of the next character that follows, from left to right, so \\* give you pattern \*.

This pattern is performed Filename Expansion, with the Pattern Matching rules.

So \* is interpret as all files starting with \ and follows by anything. In your case it matched nothing and bash left the pattern \* unchanged to echo.

0

I believe that all the other answers to the second half of the question (“What command should I use?”) are wrong, if only technically or in edge cases.  I believe that this addresses the problems in the other answers:

(set -- *; echo "\\$*")

and (FWIW) all the writing is being done in a single command
(unlike printf '\\'; printf "%s " *; echo).

  • Use a subshell to avoid setting / changing the positional parameters in an outer scope / context.
  • Naively, set * sets the positional parameters ($1, $2, $3, etc…) to the names of the files in the current directory.  (Filenames beginning with . (dot) are included, or not, based on whether the dotglob option is set.)
  • set * can fail if there is a file whose name begins with - (dash) (and it is the first file in the * list).  set -- * handles this; -- says “everything after this is an argument and not an option / flag.”
  • $* is the list of positional parameters ($1 $2 $3 …), with the parameters separated by space(s).  The \\ causes \ to be output at the beginning of the line, just as in the echo \\ * (example command in the question), but it doesn’t interfere with the expansion of the subsequent $*.
  • $* is the list of positional parameters ($1 $2 $3 …), with the parameters separated by the first character of $IFS.  If there’s a possibility that the first character of $IFS might not be space, do
(set -- *; IFS=" "; echo "\\$*")

Slightly more verbose options include

(set -- *; printf "%s\n" "\\$*")

and

(set -- *; printf "\\%s\n" "$*")

These are safer in an environment where echo might interpret backslashes in the argument strings even without an -e option.

Again, add IFS=" "; if there’s a possibility that the first character of $IFS might not be space.

-1

I ran into a special character issue which none of this seemed to resolve when attempting to transfer using one particular Windoze "direct connection" utility. So I stepped out with my caveman club and was able to make this work. The main issue was that the desitination path included an escape character prior to the dynamic filename value stored as string.

OUTPUT_FILE=`echo "my_file_name.txt"`
OUTPUT_DIR=`echo "/path/to/file/"`
xfrcmd=`echo "path_to_transferutil ${OUTPUT_DIR}${OUTPUT_FILE}" 'X:\Reports\Reconciliation"`
xfrcmd=${xfrcmd}\\
xfrcmd=$xfrcmd`echo "${OUTPUT_FILE}' windozefeller 'account,pass' label parm1 parm2;"`
eval $xfrcmd >logdest
  • I don’t quite understand what you’re doing (i.e., what you’re accomplishing; what problem you’re trying to solve).  And, maybe, if I understood that, I’d understand how this relates to the question — but I don’t.  Please do not respond in comments; edit your answer to make it clearer and more complete. P.S. Why do you say var=`echo value` instead of var=value? – Scott Aug 27 '18 at 23:32
  • The problem being solved is that with the straightforward assignment of the value and with a variable name included in the windows path building, the backslash prior to the name of the var is stripped from the path. The use of the echo command in building the command to be executed and breaking it in the middle manually to force that slash to come appended to the string before completing the assignment and executing works fine. Escaping the character in the string assignment is not as straightforward as it's been presented here and in all cases. – OtisM Aug 29 '18 at 19:19

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.