管道内的变量范围?

管道内的变量范围?

这是非常微不足道的,我确实找到了很多接近的答案,但仍然无法解决这个问题。我制作了一个带有参数的脚本,基本上类似于:

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 -cshell 不知道$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作为参数传递给它。

您的示例的另一个问题是您在单个刻度内有变量。

相关内容