Jef*_*eff 7 bash filenames stdin arguments
我希望我的脚本读取作为 glob 或 STDIN 给出的一堆文件名(可能有空格),并用它们做一些事情。我已经能够分别阅读任何一种方式,但不能将它们结合起来。
这从命令行读取 globs:
for filename in "$@"; do
process_file "$filename"
done
Run Code Online (Sandbox Code Playgroud)
这从标准输入读取:
IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}"; do
process_file "$filename"
done
Run Code Online (Sandbox Code Playgroud)
我真正想要的是从任何一个读取到一个数组中,这样我就不必将整个for filename in...循环复制两次。对不起,如果这很明显,我是 BASH 的新手。
编辑:我认为最好的办法是从 args 中读取它们,否则等待STDIN. 我该怎么做?
编辑:好的,问题不是我想的那样。问题是 process_file 也要求用户输入。有没有办法从STDINEOF 开始读取,存储它,然后再次开始要求输入?
当您没有在命令行上指定任何文件名时,文本处理工具传统上从标准输入读取输入。您可以通过测试$#变量来检查是否有命令行参数。假设process_file如果不传递参数,则从标准输入读取:
if [ $# -eq 0 ]; then
process_file
else
for x; do
process_file "$x"
done
fi
Run Code Online (Sandbox Code Playgroud)
或者,如果process_file需要参数,请尝试传递它/dev/stdin以从标准输入读取:
if [ $# -eq 0 ]; then set /dev/stdin; fi
for x; do
process_file "$x"
done
Run Code Online (Sandbox Code Playgroud)
当没有命令行参数时,您要求从标准输入读取文件名列表。当然,这是可能的,但我不建议这样做,因为这很不寻常。您的用户必须学习与大多数其他工具不同的约定。尽管如此,这是一种方法:
if [ $# -eq 0 ]; then
set -f # turn off globbing
IFS='
' # set newlines to be the only field separator
set -- $(cat)
set +f; unset IFS
fi
for x; do
process_file "$x"
done
Run Code Online (Sandbox Code Playgroud)
考虑这个小脚本:
#!/bin/bash
declare -a ARGS=("$@")
while read -rt .02 arg;do
ARGS+=($arg)
done
declare -p ARGS
Run Code Online (Sandbox Code Playgroud)
你可以打电话给他们argsAndInput.sh索取样品,然后...
seq 1 10 | ./argsAndInput.sh foo bar baz
declare -a ARGS=([0]="foo" [1]="bar" [2]="baz" [3]="1" [4]="2" [5]="3" [6]="4" [
7]="5" [8]="6" [9]="7" [10]="8" [11]="9" [12]="10")
seq 1 10 | ./argsAndInput.sh
declare -a ARGS=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8"
[8]="9" [9]="10")
./argsAndInput.sh foo bar baz
declare -a ARGS=([0]="foo" [1]="bar" [2]="baz")
./argsAndInput.sh
declare -a ARGS=()
Run Code Online (Sandbox Code Playgroud)
根据您的需要,您可以减少超时 ( read -t .02) 或使用:
while read -t 0 &&
read -r arg;do
ARGS+=($arg)
done
Run Code Online (Sandbox Code Playgroud)
如果您确定您的输入在bash 运行之前已准备好。
您可以使用xargsto转换 STDIN(即使有很多条目,比命令行缓冲区大)。
或者,也许是这样的:
if [ $# -gt 0 ] ;then
for filename in "$@" ; do
process_file "$filename"
done
else
IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}"; do
process_file "$filename"
done
fi
Run Code Online (Sandbox Code Playgroud)
或两者:
IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}" "$@" ; do
process_file "$filename"
done
fi
Run Code Online (Sandbox Code Playgroud)
或者通过添加这样的选项,例如仅-l代表行参数,以确保不会读取(等待)STDIN:
case "$1" in
-l )
shift
for filename in "$@" ; do
process_file "$filename"
done
;;
* )
for filename in "${filenames[@]}" "$@" ; do
process_file "$filename"
done
;;
esac
Run Code Online (Sandbox Code Playgroud)
(未经测试!)