这是非常微不足道的,我确实找到了很多接近的答案,但仍然无法解决这个问题。我制作了一个带有参数的脚本,基本上类似于:
var1=$1
echo "outside the pipe, my variable is set: $var1"
ls *.* | xargs sh -c 'echo "$0" "$@"; echo "inside the pipe, my variable is empty: $var1"'
我的问题是,我需要通过管道传输文件名列表以及我的一个变量。我该怎么做?我尝试将文件列表写入数组,然后附加/前置我的参数并将其传递到管道中,但这确实会在以后产生问题,因为我分块处理它,我的原始脚本类似于(imagemagick):
ls *.png | xargs -n 100 sh -c 'convert "$0" "$@" -evaluate-sequence $var1 ../out2/"$0"'
非常感谢任何帮助!
答案1
不,您不必通过管道传输文件名列表。
var1=$1
find . -maxdepth 1 -type f -name '*.png' -exec sh -c '
var1=$1; first=$2; shift 2
convert "$first" "$@" -evaluate-sequence "$var1" ../out2/"$first"' sh "$var1" {} +
也就是说,将内部子 shell$var1
作为第一个参数(第零个参数应该是 shell 的名称)。
内部 shell 去掉$var1
参数列表,并对find
找到的第一个路径名执行相同的操作(我不知道为什么,但这至少使它相当于我相信你的代码所做的),然后移走这些从$@
使用shift 2
.
如果您需要以 100 个为一批进行此操作:
var1=$1
find . -maxdepth 1 -type f -name '*.png' -print0 |
xargs -0 -n 100 sh -c '
var1=$1; first=$2; shift 2
convert "$first" "$@" -evaluate-sequence "$var1" ../out2/"$first"' sh "$var1"
这比管道输出更安全的原因ls
是因为路径名作为空终止列表而不是换行符终止列表传递。该sh
脚本在两种变体中都是相同的,并$var1
作为命令行参数。
您的代码不起作用的原因是sh -c
shell 不知道$var1
其父 shell 的情况。
有关的:
答案2
您实际上不必运行另一个 shell 来执行此操作。要处理与 glob 表达式匹配的所有文件,只需在最终要运行的程序的命令行中使用 glob 即可:
convert *.png -whatever /some/output/path
如果您只需要访问部分文件,请将它们存储在数组中并对其进行切片或索引:
files=(*.png)
convert "${files[0]}" "${files[@]:1}" -evaluate-sequence "$var1" ../out2/"${files[0]}"
("${files[0]}"
是第一个文件名,"${files[@]:1}"
扩展到所有其他文件)
从那里,我们可以一次处理一百个文件:
for (( i=0; i < "${#files[@]}" ; i+=100 )); do
convert "${files[@]:i:100}" -evaluate-sequence "$var1" ../out2/"${files[i]}"
done
也就是说,如果我xargs
正确解释了你的命令。它似乎将第一个文件名作为第零个参数传递给sh -c
(通常作为 shell 的名称)。
但是要回答您提出的问题,您可以使用export
变量使其对内壳可见,或者将其放在双引号中以使其由外壳扩展。例如
var=foo
export var
echo bar | xargs sh -c 'echo "$var" "$1"' sh
或者
var=foo
echo bar | xargs sh -c "echo '$var' "'"$1"' sh
两者都输出foo bar
。通过环境更好,因为语法更清晰(请注意后者周围$var
和$1
中的不同类型的引号),并且变量值中的任何引号都不会引起问题,就像在第二种情况下一样。
答案3
这sh -c
是一个新的 shell,因此您需要执行 of 操作export
,$var1
以便子进程拥有它或将其sh -c
作为参数传递给它。
您的示例的另一个问题是您在单个刻度内有变量。