我希望我的脚本读取一堆以 glob 或 STDIN 形式给出的文件名(可能有空格),并用它们做一些事情。我可以单独阅读这两种方式,但不能将它们结合起来。
这从命令行读取 glob:
for filename in "$@"; do
process_file "$filename"
done
这是从 STDIN 读取的:
IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}"; do
process_file "$filename"
done
我真正想要的是将其中任何一个读入数组,这样我就不必将整个for filename in...
循环重复两次。抱歉,如果这很明显,我是 BASH 新手。
编辑:我认为最好的办法是从给出的参数中读取它们,否则等待STDIN
.我该怎么做呢?
编辑:好的,问题不是我想的那样。问题是 process_file 还要求用户输入。有没有办法从STDIN
EOF 开始读取,存储它,然后再次开始要求输入?
答案1
当您未在命令行上指定任何文件名时,文本处理工具传统上会从标准输入读取输入。您可以通过测试变量来检查是否存在命令行参数$#
。假设process_file
如果不向标准输入传递参数,则从标准输入读取:
if [ $# -eq 0 ]; then
process_file
else
for x; do
process_file "$x"
done
fi
或者,如果process_file
需要参数,请尝试将其传递/dev/stdin
以从标准输入读取:
if [ $# -eq 0 ]; then set /dev/stdin; fi
for x; do
process_file "$x"
done
您要求在没有命令行参数时从标准输入读取文件名列表。当然这是可能的,但我不建议这样做,因为这是不寻常的。您的用户必须学习与大多数其他工具不同的约定。尽管如此,这里有一种方法可以做到这一点:
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
答案2
新答案2021-10-18
bash:从中创建列表争论 和/或 标准输入
考虑这个小脚本:
#!/bin/bash
declare -a ARGS=("$@")
while read -rt .02 arg;do
ARGS+=($arg)
done
declare -p ARGS
你可以打电话给他们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=()
根据您的需要,您可以减少超时 ( read -t .02
) 或使用:
while read -t 0 &&
read -r arg;do
ARGS+=($arg)
done
如果您确定您的输入已准备好前你的 bash 应该运行。
旧答案
您可以xargs
使用转换 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
或两者:
IFS=$'\n' read -d '' -r -a filenames
for filename in "${filenames[@]}" "$@" ; do
process_file "$filename"
done
fi
或者通过添加这样的选项,就像-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
(未经测试!)
答案3
这是我编写的脚本,用于安全地在 cygwin X11 gvim 和 windows gvim 中打开文件。它被设计为外壳包装器,这是执行您想要的操作的部分:
if [ ${#} -ne 0 ]; then #if we have filenames as CLArgs...
[[ ! -z ${DEBUG} ]] && echo "Got arguments [${#}]:'${@}'"
for OFILE in "${@}"; do
[[ -h ${OFILE} ]] && OFILE="$(readlink -f "${OFILE}")"
[[ ${WINVIM} == true ]] && OFILE=$(cygpath -wal "${OFILE}")
echo "\"${VIMRUN}\" --servername GVIM $RT \"${OFILE}\""
"${VIMRUN}" --servername GVIM $RT "${OFILE}" &
RT="--remote-tab"
done
else #otherwise read from stdin safely and handle spaces...
while read OFILE; do
[[ -h ${OFILE} ]] && OFILE="$(readlink -f "${OFILE}")"
[[ ${WINVIM} == true ]] && OFILE=$(cygpath -wal "${OFILE}")
echo "\"${VIMRUN}\" --servername GVIM $RT \"${OFILE}\""
"${VIMRUN}" --servername GVIM $RT "${OFILE}" &
RT="--remote-tab"
done
fi
答案4
与bash4.4
及以上:
if [ "$#" -eq 0 ]; then
readarray -d '' args
set -- "${args[@]}"
fi
for arg do
something with "$arg"
done