Могу ли я прочитать стандартный цикл предыдущей итерации в цикле Bash for stdin? - PullRequest
0 голосов
/ 29 января 2019

У меня есть 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 - означает «чтение из стандартного ввода», а 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, но следующие итерации не получат ничего в stdin:

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

Ответы [ 5 ]

0 голосов
/ 29 января 2019

Вы можете определить рекурсивную функцию:

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  
0 голосов
/ 29 января 2019

Если вы действительно хотите захватить промежуточный вывод, вы можете использовать <<< и $()

Обратите внимание, что внутренне bash для этого использует временный файл, но по крайней мере вам не нужносделайте все возможное, но с обычными икрами, если ваш контент содержит кавычки:

res1=$(cmd1)
res2=$(cmd2 <<< "$res1" )
res3=$(cmd3 <<< "$res2" )
0 голосов
/ 29 января 2019

Кажется, что-то вроде этого работает:

#!/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:-
0 голосов
/ 29 января 2019

И еще один метод с использованием 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
0 голосов
/ 29 января 2019

Это уродливая конструкция (мне не нравится 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.

...