简体   繁体   中英

single quote and $ in string bash

Im running the below script to run some checks on file paths within a function Some of the paths contains single quotes and the temporary files contain "$" If I enclose the variable in single quotes (name variable below) then the string in truncated if there is a ' in the file path. If I use a double quote around the filepath then it truncates if there is a "$" in the path. Is there any way out of this circular conundrum?

File=/root/fed/~$reader.txt

Is echoing as if there is a $ in the file path

/root/fed/eager.txt

if there is a ' in the file path and I enclose around single quote (to stop the above from happening) then

File='/root/fed/reader's'
(this wont echo)

Code is :

find / -type f -print0 | xargs -0 -I name bash -c "fnchash 'name'" 

fnchash () {
    echo "$1"
}

Single-quoted strings may not contain single quotes. Not even by escaping them. Unquoted or double-quoted and unescaped $ introduces a variable reference that expands to the value of the referenced shell variable, or to nothing if no such variable is defined.

One solution involves double quotes and escape characters. For example:

File=/root/fed/~\$reader.txt
File=/root/fed/reader\'s
File="/root/fed/\$reader's.txt"

Note, too, that quotes are not necessarily string delimiters -- they are sub string delimiters. Thus, these work, too:

File=/root/fed/~'$'reader.txt
File=/root/fed/reader"'"s

If you need to perform automatic quoting of data read at runtime, then you should also be aware of bash 's built-in printf command (which is more featureful than what you may have as a standalone command). Note in particular that the %q field descriptor performs all needed quoting on its arguments to make them re-usable as shell input.

printf '%q' $File

The way out of this conundrum is to stop trying to treat data as code. This snippet passes filenames as data (arguments) instead of trying to inject them into a code string, thereby preventing these issues entirely:

$ cat myscript 
fnchash () {
    for arg
    do
      printf "Now processing: %s\n" "$arg"
    done
}
export -f fnchash

find . -type f -exec bash -c 'fnchash "$@"' _ {} +

$ ls -1
File with space and '.txt
myscript
some file.txt

$ bash myscript 
Now processing: ./some file.txt
Now processing: ./myscript
Now processing: ./File with space and '.txt

This uses the fact that you can call bash -c 'command' argv0 argv1 argv2.. to run 'command' with the positional parameters $0 , $1 , $2 .. set to arguments passed to bash and process them as if they were arguments to a script ( man bash under -c ).

-exec bash -c 'command' _ {} + is then used to run command with $0 set to a dummy value _ and the rest of the parameters set to filenames that find finds ( man find under -exec ).

The bash command can then process these arguments just like any script would.

The same technique can also be used with xargs , here to parallelize the process 5 ways in chunks of 20 files:

find . -type f -print0 | xargs -0 -P 5 -n 20 bash -c 'fnchash "$@"' _

您可以通过使用单引号并将所有单引号替换为转义的引号来解决此问题,或者使用双引号并将所有$替换为转义的引号。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM