ls -Q 中未正确引用换行符
Newlines not quoted properly in ls -Q
将 ls -Q
与 --quoting-style=shell
一起使用,文件名中的换行符(是的,我知道...)变成了 ?
。这是一个错误吗?有没有办法以与 shell(sh 或 bash 如果可能)100% 兼容的格式获取文件名?
示例 (bash):
$ touch a$'\n'b
$ for s in literal shell shell-always c c-maybe escape locale clocale ; do
ls -Q a?b --quoting-style=$s
done
a?b
'a?b'
'a?b'
"a\nb"
"a\nb"
a\nb
‘a\nb’
‘a\nb’
也许不完全是您要找的东西,但是 "escape" 风格似乎很适合 bash
4.4 中即将推出的 ${...@E}
参数扩展。
$ touch $'a\nb' $'c\nd'
$ ls -Q --quoting-style=escape ??? | while IFS= read -r fname; do echo =="${fname@E}==="; done
==a
b==
==c
d==
这里是 man page 的相关部分(link 是原始来源):
${parameter@operator}
Parameter transformation. The expansion is either a transforma-
tion of the value of parameter or information about parameter
itself, depending on the value of operator. Each operator is a
single letter:
Q The expansion is a string that is the value of parameter
quoted in a format that can be reused as input.
E The expansion is a string that is the value of parameter
with backslash escape sequences expanded as with the
$'...' quoting mechansim.
P The expansion is a string that is the result of expanding
the value of parameter as if it were a prompt string (see
PROMPTING below).
A The expansion is a string in the form of an assignment
statement or declare command that, if evaluated, will
recreate parameter with its attributes and value.
a The expansion is a string consisting of flag values rep-
resenting parameter's attributes.
If parameter is @ or *, the operation is applied to each posi-
tional parameter in turn, and the expansion is the resultant
list. If parameter is an array variable subscripted with @ or
*, the case modification operation is applied to each member of
the array in turn, and the expansion is the resultant list.
The result of the expansion is subject to word splitting and
pathname expansion as described below.
从一些实验来看,--quoting-style=escape
似乎与包裹在 $'...'
中兼容,但有两个例外:
- 它通过在前面加上反斜杠来转义空格;但是
$'...'
不会丢弃空格前的反斜杠。
- 它不会转义单引号。
所以你也许可以这样写(在 Bash 中):
function ls-quote-shell () {
ls -Q --quoting-style=escape "$@" \
| while IFS= read -r filename ; do
filename="${filename//'\ '/ }" # unescape spaces
filename="${filename//"'"/\'}" # escape single-quotes
printf "$'%s'\n" "$filename"
done
}
为了测试这个,我创建了一个目录,里面有一堆带有奇怪字符的文件名;和
eval ls -l $(ls-quote-shell)
按预期工作。 . .虽然我不会对此做出任何坚定的保证。
或者,这是一个使用 printf
处理转义然后 printf %q
以 shell 友好的方式重新转义的版本:
function ls-quote-shell () {
ls -Q --quoting-style=escape "$@" \
| while IFS= read -r escaped_filename ; do
escaped_filename="${escaped_filename//'\ '/ }" # unescape spaces
escaped_filename="${escaped_filename//'%'/%%}" # escape percent signs
# note: need to save in variable, rather than using command
# substitution, because command substitution strips trailing newlines:
printf -v filename "$escaped_filename"
printf '%q\n' "$filename"
done
}
但如果发现第一个版本处理不当,那么第二个版本很可能也会有同样的问题。 (FWIW,eval ls -l $(ls-quote-shell)
两个版本都按预期工作。)
coreutils 8.25 具有新的 'shell-escape' 引用样式,实际上默认启用它以允许 ls 的输出始终可用,并且可以安全地复制并粘贴回其他命令。
将 ls -Q
与 --quoting-style=shell
一起使用,文件名中的换行符(是的,我知道...)变成了 ?
。这是一个错误吗?有没有办法以与 shell(sh 或 bash 如果可能)100% 兼容的格式获取文件名?
示例 (bash):
$ touch a$'\n'b
$ for s in literal shell shell-always c c-maybe escape locale clocale ; do
ls -Q a?b --quoting-style=$s
done
a?b
'a?b'
'a?b'
"a\nb"
"a\nb"
a\nb
‘a\nb’
‘a\nb’
也许不完全是您要找的东西,但是 "escape" 风格似乎很适合 bash
4.4 中即将推出的 ${...@E}
参数扩展。
$ touch $'a\nb' $'c\nd'
$ ls -Q --quoting-style=escape ??? | while IFS= read -r fname; do echo =="${fname@E}==="; done
==a
b==
==c
d==
这里是 man page 的相关部分(link 是原始来源):
${parameter@operator}
Parameter transformation. The expansion is either a transforma-
tion of the value of parameter or information about parameter
itself, depending on the value of operator. Each operator is a
single letter:
Q The expansion is a string that is the value of parameter
quoted in a format that can be reused as input.
E The expansion is a string that is the value of parameter
with backslash escape sequences expanded as with the
$'...' quoting mechansim.
P The expansion is a string that is the result of expanding
the value of parameter as if it were a prompt string (see
PROMPTING below).
A The expansion is a string in the form of an assignment
statement or declare command that, if evaluated, will
recreate parameter with its attributes and value.
a The expansion is a string consisting of flag values rep-
resenting parameter's attributes.
If parameter is @ or *, the operation is applied to each posi-
tional parameter in turn, and the expansion is the resultant
list. If parameter is an array variable subscripted with @ or
*, the case modification operation is applied to each member of
the array in turn, and the expansion is the resultant list.
The result of the expansion is subject to word splitting and
pathname expansion as described below.
从一些实验来看,--quoting-style=escape
似乎与包裹在 $'...'
中兼容,但有两个例外:
- 它通过在前面加上反斜杠来转义空格;但是
$'...'
不会丢弃空格前的反斜杠。 - 它不会转义单引号。
所以你也许可以这样写(在 Bash 中):
function ls-quote-shell () {
ls -Q --quoting-style=escape "$@" \
| while IFS= read -r filename ; do
filename="${filename//'\ '/ }" # unescape spaces
filename="${filename//"'"/\'}" # escape single-quotes
printf "$'%s'\n" "$filename"
done
}
为了测试这个,我创建了一个目录,里面有一堆带有奇怪字符的文件名;和
eval ls -l $(ls-quote-shell)
按预期工作。 . .虽然我不会对此做出任何坚定的保证。
或者,这是一个使用 printf
处理转义然后 printf %q
以 shell 友好的方式重新转义的版本:
function ls-quote-shell () {
ls -Q --quoting-style=escape "$@" \
| while IFS= read -r escaped_filename ; do
escaped_filename="${escaped_filename//'\ '/ }" # unescape spaces
escaped_filename="${escaped_filename//'%'/%%}" # escape percent signs
# note: need to save in variable, rather than using command
# substitution, because command substitution strips trailing newlines:
printf -v filename "$escaped_filename"
printf '%q\n' "$filename"
done
}
但如果发现第一个版本处理不当,那么第二个版本很可能也会有同样的问题。 (FWIW,eval ls -l $(ls-quote-shell)
两个版本都按预期工作。)
coreutils 8.25 具有新的 'shell-escape' 引用样式,实际上默认启用它以允许 ls 的输出始终可用,并且可以安全地复制并粘贴回其他命令。