在 Bash for 循环中,我可以将前一次迭代的标准输出读取为标准输入吗?
In a Bash for-loop, can I read the previous iteration's stdout as stdin?
我有一个 bash oneliner,我在其中多次通过相同的 ImageMagick 操作对一张图像进行管道传输。我想将其作为 Bash 循环来执行,最好不使用临时文件。
一线是:cat in.jpg | convert -quality 1 - jpg:- | convert -quality 1 - jpg:- | convert -quality 1 - jpg:- > out.jpg
这个也有效:(convert -quality 1 - jpg:- | convert -quality 1 - jpg:- | convert -quality 1 - jpg:-) > out.jpg < in.jpg
在 convert
命令中,-
表示 "read from stdin",jpg:-
表示 "output in JPEG format to stdout"。
我试过 cat in.jpg | for x in {1..3}; do convert -quality 1 - jpg:-; done > out.jpg
和 (for x in {1..3}; do convert -quality 1 - jpg:-; done) < in.jpg > out.jpg
,但都给出相同的错误。
我希望获得应用了所有 3 个操作的输出文件,但我得到的是只应用了一个操作的输出文件和以下错误,我认为这意味着第一次迭代到达读取 in.jpg 文件,但以下迭代不会在标准输入上获得任何内容:
convert: no decode delegate for this image format `' @ > error/constitute.c/ReadImage/501.
convert: no images defined `jpg:-' @ error/convert.c/ConvertImageCommand/3210.
convert: no decode delegate for this image format `' @ error/constitute.c/ReadImage/501.
convert: no images defined `jpg:-' @ error/convert.c/ConvertImageCommand/3210.
(我通过 运行 (for x in 90 30 1; do convert -quality $x - jpg:-; done) < in.jpg > out.jpg
确认这只是第一次接触输出文件的迭代
这是一个丑陋的结构(我不喜欢 eval
),但会解决您的问题:
cmd=()
for i in {1..3}
do
cmd+=("convert" "-quality" "1" "-" "jpg:-" "|")
done
unset "cmd[${#cmd[@]}-1]"
eval "< in.jpg ${cmd[@]} > out.jpg"
您创建了一个数组 calles cmd
。然后,您使用循环将命令的组件和管道符号添加到数组中。创建此数组后,删除最后一个元素(不需要的额外管道)。最后,您 eval
输入重定向、创建的命令行和输出重定向的串联。
eval
是必需的,否则 |
将被视为文字,不会创建管道,而是作为参数传递给 convert
。
类似这样的方法似乎有效:
#!/bin/bash
cmd="cat input.jpg "
for ((iter=0;iter<15;iter++)) do
cmd+=" | convert jpg:- -quality 50 jpg:- "
done
echo $cmd
bash -c "$cmd" > result.jpg
实际执行命令的调试输出为:
cat input.jpg | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:-
另一种使用eval
的方法:
# wrap command to be run into a shell function
# it should read from stdin and write to stdout
pipecmd(){ convert -quality 1 - jpg:-; }
# generate appropriate number of copies
cmds=()
for x in {1..3}; do cmds[$x]=pipecmd; done
# flatten list into a single string
cmdlist="${cmds[@]}"
# insert the pipes
pipeline="${cmdlist// /|}"
# create a shell function that uses this pipeline
# we need eval so $pipeline is expanded properly
eval "runpipeline(){ $pipeline; }"
# or combine the previous two steps
eval "runpipeline2(){ ${cmdlist// /|}; }"
# check what we created
type pipecmd
type runpipeline
type runpipeline2
# do something
runpipeline <in.jpg >out.jpg
# this will hang!
runpipeline in.jpg >out.jpg
# or just use the wrapped command directly
pipecmd <in.jpg | pipecmd | pipecmd >out.jpg
如果你真的想捕获中间输出,你可以使用 <<<
和 $()
请注意,在内部 bash 确实为此使用了一个临时文件,但至少您不必执行这些操作,但如果您的内容包含引号,则使用通常的 caviats:
res1=$(cmd1)
res2=$(cmd2 <<< "$res1" )
res3=$(cmd3 <<< "$res2" )
你可以定义一个递归函数:
rec_convert () {
n=
if [ $n -eq 0 ]; then
cat
else
convert -quality 1 - jpg:- | rec_convert $((n - 1))
fi
}
rec_convert 3 < in.jpg > out.jpg
我有一个 bash oneliner,我在其中多次通过相同的 ImageMagick 操作对一张图像进行管道传输。我想将其作为 Bash 循环来执行,最好不使用临时文件。
一线是:cat in.jpg | convert -quality 1 - jpg:- | convert -quality 1 - jpg:- | convert -quality 1 - jpg:- > out.jpg
这个也有效:(convert -quality 1 - jpg:- | convert -quality 1 - jpg:- | convert -quality 1 - jpg:-) > out.jpg < in.jpg
在 convert
命令中,-
表示 "read from stdin",jpg:-
表示 "output in JPEG format to stdout"。
我试过 cat in.jpg | for x in {1..3}; do convert -quality 1 - jpg:-; done > out.jpg
和 (for x in {1..3}; do convert -quality 1 - jpg:-; done) < in.jpg > out.jpg
,但都给出相同的错误。
我希望获得应用了所有 3 个操作的输出文件,但我得到的是只应用了一个操作的输出文件和以下错误,我认为这意味着第一次迭代到达读取 in.jpg 文件,但以下迭代不会在标准输入上获得任何内容:
convert: no decode delegate for this image format `' @ > error/constitute.c/ReadImage/501.
convert: no images defined `jpg:-' @ error/convert.c/ConvertImageCommand/3210.
convert: no decode delegate for this image format `' @ error/constitute.c/ReadImage/501.
convert: no images defined `jpg:-' @ error/convert.c/ConvertImageCommand/3210.
(我通过 运行 (for x in 90 30 1; do convert -quality $x - jpg:-; done) < in.jpg > out.jpg
这是一个丑陋的结构(我不喜欢 eval
),但会解决您的问题:
cmd=()
for i in {1..3}
do
cmd+=("convert" "-quality" "1" "-" "jpg:-" "|")
done
unset "cmd[${#cmd[@]}-1]"
eval "< in.jpg ${cmd[@]} > out.jpg"
您创建了一个数组 calles cmd
。然后,您使用循环将命令的组件和管道符号添加到数组中。创建此数组后,删除最后一个元素(不需要的额外管道)。最后,您 eval
输入重定向、创建的命令行和输出重定向的串联。
eval
是必需的,否则 |
将被视为文字,不会创建管道,而是作为参数传递给 convert
。
类似这样的方法似乎有效:
#!/bin/bash
cmd="cat input.jpg "
for ((iter=0;iter<15;iter++)) do
cmd+=" | convert jpg:- -quality 50 jpg:- "
done
echo $cmd
bash -c "$cmd" > result.jpg
实际执行命令的调试输出为:
cat input.jpg | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:- | convert jpg:- -quality 50 jpg:-
另一种使用eval
的方法:
# wrap command to be run into a shell function
# it should read from stdin and write to stdout
pipecmd(){ convert -quality 1 - jpg:-; }
# generate appropriate number of copies
cmds=()
for x in {1..3}; do cmds[$x]=pipecmd; done
# flatten list into a single string
cmdlist="${cmds[@]}"
# insert the pipes
pipeline="${cmdlist// /|}"
# create a shell function that uses this pipeline
# we need eval so $pipeline is expanded properly
eval "runpipeline(){ $pipeline; }"
# or combine the previous two steps
eval "runpipeline2(){ ${cmdlist// /|}; }"
# check what we created
type pipecmd
type runpipeline
type runpipeline2
# do something
runpipeline <in.jpg >out.jpg
# this will hang!
runpipeline in.jpg >out.jpg
# or just use the wrapped command directly
pipecmd <in.jpg | pipecmd | pipecmd >out.jpg
如果你真的想捕获中间输出,你可以使用 <<<
和 $()
请注意,在内部 bash 确实为此使用了一个临时文件,但至少您不必执行这些操作,但如果您的内容包含引号,则使用通常的 caviats:
res1=$(cmd1)
res2=$(cmd2 <<< "$res1" )
res3=$(cmd3 <<< "$res2" )
你可以定义一个递归函数:
rec_convert () {
n=
if [ $n -eq 0 ]; then
cat
else
convert -quality 1 - jpg:- | rec_convert $((n - 1))
fi
}
rec_convert 3 < in.jpg > out.jpg