从文件中读取带有 glob 的参数列表
Reading an argument list with a glob from a file
我正在尝试从文件中读取 ll
命令的参数。
我有文件 args.txt
,内容 some?
。我有名称为 some1
、some2
和 some3
的文件以及一些其他文件。当我执行 cat args.txt | ll
时,我得到的结果与我执行 ll
.
时的结果相同
谁能解释一下为什么会这样,我怎样才能达到预期的结果。
提前致谢。
您可以使用 xargs
将 cat 的输出传递给命令(在您的情况下为 ll
):
cat args.txt | xargs ll
如果您的 args.txt 包含多个值,用换行符或 space 分隔,xargs
将分别传递每个值,执行命令的次数与 args.txt:
例如,如果您的 args.txt 具有以下内容:
/var/
/usr/
/usr /var
那么执行的结果会是这样的:
$ cat args.txt | xargs ls
/usr:
bin etc games include lib lib64 libexec local sbin share src tmp
/usr/:
bin etc games include lib lib64 libexec local sbin share src tmp
/var:
adm cache crash cvs db empty games gopher kerberos lib local lock log mail nis opt preserve run spool tmp var yp
/var/:
adm cache crash cvs db empty games gopher kerberos lib local lock log mail nis opt preserve run spool tmp var yp
注意:我假设 ll
是 别名 ls -l
或一些变体。
正如 Charles Duffy 指出的那样,(默认情况下)只有 interactive shells 知道别名,而脚本以及 xargs
等工具不知道它们。
通过管道发送到命令的内容是通过其 stdin 流接收的,这与命令的 arguments(选项例如 -l
和操作数例如 some?
).
因此,需要显式转换才能将文件 args.txt
的内容转换为 参数 您可以传递给ls -l
:
ls -l $(cat args.txt) # In Bash you can simplify to: ls -l $(< args.txt)
请注意,命令替换 ($(...)
) 是故意 未加引号 以确保 globbing(文件名扩展)根据需要应用于 args.txt
的内容。
你的情况很少见,这种技术 脆弱 - 实际上 需要 。
为了说明脆弱性:例如,如果您的 globbing 模式是 "some file"?
(您需要模式的双引号仍被识别为单个参数),该命令将不起作用不再是了,因为 "
字符在它们是命令替换(或变量扩展)的结果时失去了它们的语法功能。
将 stdin 或文件内容转换为参数的 标准实用程序是 xargs
。但是,在您的特定情况下,它不是一个选项,因为您的文件包含一个glob(文件名模式)必须由 shell 扩展,但 xargs
仅调用外部实用程序,不涉及 shell:
$ xargs ls -l < args.txt # !! Does NOT work as intended here.
ls: file?: No such file or directory
文件名 file?
被 按字面意思 传递给 ls
(实际上不存在名为 file?
的文件)- 没有发生通配,因为没有shell 参与其中。
以下内容有点矫枉过正,但无论 shell 配置如何,都竭尽全力保持正确和一致,并处理所有可能的文件名(即使是带有空格的文件名):
# note f() ( ) instead of f() { }; this runs in a subshell, so its changes to IFS or
# shopt settings don't modify behavior of the larger shell.
globfiles() (
set +f ## ensure that globbing is enabled
shopt -u nullglob failglob ## ensure that non-default options on how to handle failed
## ...glob attempts are both disabled
IFS= ## disable string-splitting
while IFS= read -r -d '' filename; do
printf '%s[=10=]' $filename ## unquoted use -> expand as glob
## ...expands format string once per argument, hence once per
## ...file found. (No string-splitting due to prior IFS=)
done
)
...此后用作(如果您的输入文件以换行符分隔):
tr '\n' '[=11=]' <args.txt | globfiles | xargs -0 ls -l --
...或者(如果您的输入文件是 NUL 分隔的——这是理想的,因为这允许引用包含文字换行符的文件名):
globfiles <args.0sv | xargs -0 ls -l --
我正在尝试从文件中读取 ll
命令的参数。
我有文件 args.txt
,内容 some?
。我有名称为 some1
、some2
和 some3
的文件以及一些其他文件。当我执行 cat args.txt | ll
时,我得到的结果与我执行 ll
.
谁能解释一下为什么会这样,我怎样才能达到预期的结果。 提前致谢。
您可以使用 xargs
将 cat 的输出传递给命令(在您的情况下为 ll
):
cat args.txt | xargs ll
如果您的 args.txt 包含多个值,用换行符或 space 分隔,xargs
将分别传递每个值,执行命令的次数与 args.txt:
例如,如果您的 args.txt 具有以下内容:
/var/
/usr/
/usr /var
那么执行的结果会是这样的:
$ cat args.txt | xargs ls
/usr:
bin etc games include lib lib64 libexec local sbin share src tmp
/usr/:
bin etc games include lib lib64 libexec local sbin share src tmp
/var:
adm cache crash cvs db empty games gopher kerberos lib local lock log mail nis opt preserve run spool tmp var yp
/var/:
adm cache crash cvs db empty games gopher kerberos lib local lock log mail nis opt preserve run spool tmp var yp
注意:我假设 ll
是 别名 ls -l
或一些变体。
正如 Charles Duffy 指出的那样,(默认情况下)只有 interactive shells 知道别名,而脚本以及 xargs
等工具不知道它们。
通过管道发送到命令的内容是通过其 stdin 流接收的,这与命令的 arguments(选项例如 -l
和操作数例如 some?
).
因此,需要显式转换才能将文件 args.txt
的内容转换为 参数 您可以传递给ls -l
:
ls -l $(cat args.txt) # In Bash you can simplify to: ls -l $(< args.txt)
请注意,命令替换 ($(...)
) 是故意 未加引号 以确保 globbing(文件名扩展)根据需要应用于 args.txt
的内容。
你的情况很少见,这种技术 脆弱 - 实际上 需要 。
为了说明脆弱性:例如,如果您的 globbing 模式是 "some file"?
(您需要模式的双引号仍被识别为单个参数),该命令将不起作用不再是了,因为 "
字符在它们是命令替换(或变量扩展)的结果时失去了它们的语法功能。
将 stdin 或文件内容转换为参数的 标准实用程序是 xargs
。但是,在您的特定情况下,它不是一个选项,因为您的文件包含一个glob(文件名模式)必须由 shell 扩展,但 xargs
仅调用外部实用程序,不涉及 shell:
$ xargs ls -l < args.txt # !! Does NOT work as intended here.
ls: file?: No such file or directory
文件名 file?
被 按字面意思 传递给 ls
(实际上不存在名为 file?
的文件)- 没有发生通配,因为没有shell 参与其中。
以下内容有点矫枉过正,但无论 shell 配置如何,都竭尽全力保持正确和一致,并处理所有可能的文件名(即使是带有空格的文件名):
# note f() ( ) instead of f() { }; this runs in a subshell, so its changes to IFS or
# shopt settings don't modify behavior of the larger shell.
globfiles() (
set +f ## ensure that globbing is enabled
shopt -u nullglob failglob ## ensure that non-default options on how to handle failed
## ...glob attempts are both disabled
IFS= ## disable string-splitting
while IFS= read -r -d '' filename; do
printf '%s[=10=]' $filename ## unquoted use -> expand as glob
## ...expands format string once per argument, hence once per
## ...file found. (No string-splitting due to prior IFS=)
done
)
...此后用作(如果您的输入文件以换行符分隔):
tr '\n' '[=11=]' <args.txt | globfiles | xargs -0 ls -l --
...或者(如果您的输入文件是 NUL 分隔的——这是理想的,因为这允许引用包含文字换行符的文件名):
globfiles <args.0sv | xargs -0 ls -l --