Разделить вход для другой команды и объединить результат

8

Я знаю, как объединить результат другой команды

paste -t',' <(commanda) <(commandb)

Я знаю, что один и тот же вход для другой команды

cat myfile | tee >(commanda) >(commandb)

Теперь, как объединить эти команды? Так что я могу сделать

cat myfile | tee >(commanda) >(commandb) | paste -t',' resulta resultb

Скажи у меня есть файл

мой файл:

1
2
3
4

Я хочу сделать новый файл

1 4 2
2 3 4
3 2 6
4 1 8

я использовал

cat myfile | tee >(tac) >(awk '{print $1*2}') | paste

даст мне результат по вертикали, где я действительно хочу вставить их в горизонтальном порядке.

user40129
источник
вам может потребоваться написать два потока для разделения каналов имен и объединить их с программой монитора.
盐 友情 留 在 无 盐

Ответы:

8

Когда вы используете несколько замен процесса, вы не гарантируете получение результата в каком-то определенном порядке, поэтому вам лучше придерживаться

paste -t',' <(commanda < file) <(commandb < file)

Предполагая, что cat myfileстоит какой-то дорогой конвейер, я думаю, что вам придется хранить выходные данные либо в файле, либо в переменной:

output=$( some expensive pipeline )
paste -t',' <(commanda <<< "$output") <(commandb <<< "$output")

Используя ваш пример:

output=$( seq 4 )
paste -d' ' <(cat <<<"$output") <(tac <<<"$output") <(awk '$1*=2' <<<"$output")
1 4 2
2 3 4
3 2 6
4 1 8

Еще одна мысль: FIFO и один трубопровод

mkfifo resulta resultb
seq 4 | tee  >(tac > resulta) >(awk '$1*=2' > resultb) | paste -d ' ' - resulta resultb
rm resulta resultb
1 4 2
2 3 4
3 2 6
4 1 8
Гленн Джекман
источник
Могу ли я использовать что-то вроде / dev / fd / 3-9?
user40129
4

yashОболочка имеет уникальные особенности ( переназначения трубопровода и переназначение процесса ) , которые делают , что легче там:

cat myfile | (
  exec 3>>|4
  tee /dev/fd/5 5>(commanda >&3 3>&-) 3>&- |
    commandb 3>&- |
    paste -d , /dev/fd/4 - 3>&-
)

3>>|4( перенаправление конвейера ) создает канал, где конец записи находится на fd 3, а конец чтения на fd 4.

3>(commanda>&3)это перенаправление процесса , немного похоже на подстановку процесса ksh / zsh / bash, но просто выполняет перенаправление и не заменяет его на /dev/fd/n. ksh«s» >(cmd)более или менее совпадает с yash«s» n>(cmd) /dev/fd/n(есть nвыбранный дескриптор файла, kshнад которым у вас нет контроля).

Стефан Шазелас
источник
3

С zsh:

pee() (
  n=0 close_in= close_out= inputs=() outputs=()
  merge_command=$1; shift
  for cmd do
    eval "coproc $cmd $close_in $close_out"

    exec {i}<&p {o}>&p
    inputs+=($i) outputs+=($o)
    eval i$n=$i o$n=$o
    close_in+=" {i$n}<&-" close_out+=" {o$n}>&-"
    ((n++))
  done
  coproc :
  read -p
  eval tee /dev/fd/$^outputs $close_in "> /dev/null &
    " exec $merge_command /dev/fd/$^inputs $close_out
)

Тогда используйте как:

$ echo abcd | pee 'paste -d,' 'tr a A' 'tr b B' 'tr c C'
Abcd,aBcd,abCd

Это адаптировано из этого другого вопроса, где вы найдете некоторые подробные объяснения и подсказки об ограничениях (остерегайтесь тупиков!).

Стефан Шазелас
источник
0

Для вашего конкретного примера не должно быть нужды pasteи всего остального. Часто бывает так, что когда мы сталкиваемся с ограничением стандартного набора инструментов, это происходит потому, что то, что мы хотим сделать одним способом, можно сделать другим. Такие как:

set 1 2 3 4
while [ "$#" -gt 0 ]
do    echo "$1" "$#" "$(($1*2))"
shift;done

... который печатает ...

1 4 2
2 3 4
3 2 6
4 1 8

Вы можете получить файл с содержимым, как вы упомянули в "$@"массиве оболочки, как ...

set -f; IFS='
'; set -- $(cat)

И чтобы проверить значения arg в цикле, подобном приведенному выше, вы можете немного изменить первоначальный тест ...

while { [ "$1" -eq "${1-1}" ] ;} 2>&"$((2+!$#))"
do    echo "$1" "$#" "$(($1*2))"
shift;done  3>/dev/null >outfile

... который печатает ошибку в stderr, только если строка, считываемая с, set -- $(cat)содержит строку, которая не состоит целиком из одного целого числа.

mikeserv
источник