【问题标题】:In a Bash for-loop, can I read the previous iteration's stdout as stdin?在 Bash for 循环中,我可以将上一次迭代的标准输出读取为标准输入吗?
【发布时间】:2019-01-29 10:12:54
【问题描述】:

我有一个 bash oneliner,我在其中多次通过相同的 ImageMagick 操作对一张图像进行管道传输。我想将其作为 Bash 循环执行,最好不使用临时文件。

oneliner 是: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 命令中,- 表示“从标准输入读取”,jpg:- 表示“以 JPEG 格式输出到标准输出”。

我尝试过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确认这只是第一次迭代触及输出文件

【问题讨论】:

  • 你到底想用那个命令做什么?
  • 我觉得我不需要向你解释我的艺术,沃伦。 ????
  • @MarkSetchell 抛开所有的玩笑不谈,我正在探索用于艺术和/或喜剧目的的有趣压缩文物。????
  • @MarkSetchell 实验表明,ImageMagick 很聪明,可以在以与现有质量相同的质量保存图像时重新压缩图像。添加中间 PNG 转换有帮助。

标签: bash loops imagemagick pipe


【解决方案1】:

你可以定义一个递归函数:

rec_convert () {
  n=$1
  if [ $n -eq 0 ]; then
    cat
  else
    convert -quality 1 - jpg:- | rec_convert $((n - 1))
  fi
}

rec_convert 3 < in.jpg > out.jpg  

【讨论】:

  • 酷 - 喜欢它!
  • 哦!好优雅!
  • 请注意,这可能不适合n 的大值。
【解决方案2】:

这是一个丑陋的结构(我不喜欢eval),但会解决你的问题:

cmd=()
for i in {1..3}
do
    cmd+=("convert" "-quality" "1" "-" "jpg:-" "|")
done
unset "cmd[${#cmd[@]}-1]"

eval "< in.jpg ${cmd[@]} > out.jpg"

您创建了一个名为cmd 的数组。然后,您使用循环将命令的组件和管道符号添加到数组中。创建此数组后,删除最后一个元素(不需要的额外管道)。最后,eval 包含输入重定向、创建的命令行和输出重定向。

eval 是必需的,否则| 被视为文字,不会创建管道,而是作为弧形传递给convert

【讨论】:

  • 哈哈,同样的想法!
  • 争论点,但如果你想避免评估,你可以将文本传送到| bash。正如我祖父常说的那样,六个中的一个,六个中的另一个。
  • 你说得对,我可以用一个 eval 来换取一个额外的进程开始 :-)
【解决方案3】:

这样的事情似乎有效:

#!/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:-

【讨论】:

  • 数组更好,但bash -c 对我来说似乎是个好计划。
【解决方案4】:

还有另一种使用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

【讨论】:

    【解决方案5】:

    如果你真的想捕获中间输出,你可以使用&lt;&lt;&lt;$()

    请注意,内部 bash 确实为此使用了一个临时文件,但至少您不必这样做,但如果您的内容包含引号,则使用通常的caviats:

    res1=$(cmd1)
    res2=$(cmd2 <<< "$res1" )
    res3=$(cmd3 <<< "$res2" )
    

    【讨论】:

      猜你喜欢
      • 2019-09-01
      • 2013-03-18
      • 2016-01-14
      • 1970-01-01
      • 2011-01-15
      • 2012-04-25
      • 1970-01-01
      • 2012-06-08
      • 1970-01-01
      相关资源
      最近更新 更多