xargs: замена команды $(…) на pipe не работает

#bash #shell #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 — gt; gt; 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 , и она тоже не работает. До сих пор не могу понять, почему.

Ответ №1:

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 (здесь {} ) правильным содержимым.

Комментарии:

1. Лучшей формулировкой было бы echo "aaa111 bbb111" | xargs -I @@ bash -c 'temp="@@"; echo ${temp//111/222}'

2. Хотя это и правильно, цель здесь состояла в том, чтобы использовать ту же команду, что и в OP.

Ответ №2:

$(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 {}  

Ответ №3:

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

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

Ответ №4:

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

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

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