xargs: подстановка команды $ () на канал не работает

Я пытаюсь написать короткий сценарий и следующую команду:

echo "aaa111 bbb111" | xargs -I {} echo {} | sed 's/111/222/g'

возвращает aaa222 bbb222, чего я и ожидал.

Я ожидал следующей команды:

echo "aaa111 bbb111" | xargs -I {} echo $(echo {} | sed 's/111/222/g')

вернуть то же самое, но возвращает aaa111 bbb111! Это почему?


UPD: Чего я пытаюсь достичь:

У меня много файлов, таких как pic30-coff-gcc, pic30-coff-ag и т. Д., И мне нужно сделать символическую ссылку для каждого файла, например pic30-gcc -> pic30-coff-gcc и т. Д.

Итак, я написал это:

ls|grep 'coff-'|xargs -I {} ln -s {} $(echo {} | sed 's/coff-//g')

Это не работает: для каждого файла он сообщает, что файл существует. Я проверил команду так:

ls|grep 'coff-'|xargs -I {} echo "ln -s {} $(echo {} | sed 's/coff-//g')"

И да, часть sed не работает:

ln -s pic30-coff-gcc pic30-coff-gcc
ln -s pic30-coff-gcc-4.0.3 pic30-coff-gcc-4.0.3
...

Но если я просто напечатаю

echo "ln -s pic30-coff-gcc $(echo pic30-coff-gcc | sed 's/coff-//g')"

оно работает:

ln -s pic30-coff-gcc pic30-gcc

Затем я написал тестовую команду с aaa111, и она тоже не работает. Все еще не могу понять, почему.


person Dmitry Frank    schedule 23.03.2014    source источник


Ответы (4)


Кажется, что все, что вам нужно, это простой цикл:

for file in *coff*; do
  ln -s "${file}" "${file/coff-/}"
done
person devnull    schedule 23.03.2014

Ответ цикла for - это именно то, что я пришел сюда не читать. Вся цель xargs - избежать этого цикла. xargs, возможно, не самый умный инструмент в данном конкретном случае, но вопрос был именно в этом.

Вот решение, к которому я пришел. Возможно, это не идеально, но тем не менее работает:

echo "aaa111 bbb111" | xargs -I {} sh -c "echo \$(echo {} | sed 's/111/222/g')"
# Outputs aaa222 bbb222

Вы можете придумать разные варианты одной и той же команды:

echo "aaa111 bbb111" | xargs -I {} sh -c 'echo $(echo {} | sed "s/111/222/g")'
echo "aaa111 bbb111" | xargs -I {} sh -c "echo \`echo {} | sed 's/111/222/g'\`"

Главное здесь - просто вызвать новую оболочку, которая выполнит подстановку команд после того, как xargs заменит replace-str (здесь {}) правильным содержимым.

person Jerska    schedule 31.03.2015
comment
Лучшей формулировкой было бы echo "aaa111 bbb111" | xargs -I @@ bash -c 'temp="@@"; echo ${temp//111/222}' - person akhan; 21.11.2018
comment
Хотя это и верно, цель здесь заключалась в том, чтобы иметь ту же команду, что и OP. - person Jerska; 27.11.2018

$(echo {} | sed 's/111/222/g') оценивается перед передачей xargs в качестве параметра. Он вернет {}.

Следовательно:

echo "aaa111 bbb111" | xargs -I {} echo $(echo {} | sed 's/111/222/g')

такой же как

echo "aaa111 bbb111" | xargs -I {} echo {}
person ivicaa    schedule 23.03.2014

Вот еще один вариант, который изменяет каждую строку ввода для печати источника и назначения, а затем заставляет xargs читать два аргумента за раз.

# aside: avoid ls | grep
printf '%s\n' coff-* |
sed 's/\(coff-\(.*\)\)/\1 \2/' |
xargs -n 2 ln -s

Это явно хрупко; если у вас есть имена файлов, имена которых содержат пробелы, они будут неправильно проанализированы.

person tripleee    schedule 18.02.2021