Как выразить «сопоставить команду linux каждой строке в файле»? - PullRequest
1 голос
/ 24 сентября 2010

Часто мне нужно удалить все файлы не в определенном исходном дереве SVN.Чтобы получить список всех их имен файлов, я использую:

svn st | grep ^? | awk '{print $2}'

Эта команда выдаст мне список имен файлов, по одному имени в строке.Тогда как я могу выразить идею

for (each line in ${svn st | grep ^? | awk '{print $2}' )
    rm -f line

?

Ответы [ 5 ]

5 голосов
/ 24 сентября 2010

Использовать xargs:

svn st | grep '^?' | awk '{print $2}' | xargs rm -rf

Примечание. В вашей команде вы не имеете дело с файлами, имеющими пробелы из-за {print $2}.Вы также должны быть осторожны с xargs, поскольку он разделяет свой ввод на пробельные символы.Поэтому безопаснее использовать параметр -0, если в именах файлов есть пробелы.

Это пример более правильного использования xargs:

find -type f| tr \\n \\0 | xargs -0 wc

Или, используя параметр find -print0:

find -type f -print0 | xargs -0 wc 

xargs является одним изсамые мощные инструменты UNIX.Я использую это каждый день.

2 голосов
/ 25 сентября 2010

Почему так много людей вызывают grep в конвейере и либо используют for петли *1003*, либо xargs на заднем конце, когда у них есть кровавый awk прямо всередина вещей?

Сначала давайте избавимся от причудливого использования grep:

svn st | awk '/^?/ { print $2 }'

Поскольку awk позволяет фильтровать строки на основе регулярных выражений, использование grepсовершенно ненужно.Регулярные выражения awk ничем не отличаются от grep (в зависимости от того, какую реализацию awk и какую из grep вы используете), так зачем добавлять целый новый процесс и совершенно новое узкое место вpipe?

Оттуда у вас уже есть два варианта, которые будут короче и более читабельными:

# option 1
svn st | awk '/^?/ { print $2 }' | xargs rm -f

# option 2
rm -f $(svn st | awk '/^?/ { print $2 }')

Теперь этот второй вариант будет работать, только если ваш список файлов не превышает максимальную командуразмер строки, поэтому я рекомендую версию xargs.

Или, возможно, даже лучше, снова используйте awk.

svn st | awk '/^?/ { system("rm -f $2") }'

Это будет функциональный эквивалент того, что вы сделали выше сfor петля.Это гротескно неэффективно, поскольку он выполняет rm один раз для каждого файла, но, по крайней мере, более читабельно, чем ваш пример цикла for.Однако его можно улучшить еще дальше.Я не буду вдаваться в подробности здесь, но вместо этого я дам вам комментарии в качестве подсказки относительно того, как будет выглядеть окончательное решение.

svn st | awk 'BEGIN{ /*set up an array*/ }; /^?/ { /*add $2 to the array*/ }; END{ /*system("rm -f ...") safe chunks of the array*/}

ОК, так что последнее будет немного глотком ислишком много, чтобы печатать как обычный однострочный текст.Однако, поскольку вам приходится «часто» делать это, не так уж и плохо поместить это в скрипт:

#!/usr/bin/env awk
BEGIN {
  /* set up your accumulator array */
}

/^?/ {
  /* add $2 to the array */
}

END {
  /* invoke system("rm -f") on safe chunks of the accumulator array */
}

Теперь ваша командная строка будет svn st | myawkscript.

Теперь я предупрежу вас, что у меня нет возможности проверить все это (поскольку я избегаю SVN и CVS, как я избегаю MS-DOS - и по той же причине).Например, вам может понадобиться использовать строку #! в сценарии или регулярное выражение, которое вы используете для фильтрации, но общий принцип остается примерно таким же.А мне лично?Я бы использовал svn st | awk '/^?/ { print $2 }' | xargs rm -f для чего-то, что я делаю редко.Я делаю полный сценарий только для того, что я делаю несколько раз в неделю или больше.

0 голосов
/ 29 сентября 2010

xargs ваш друг:

svn st | awk '/^?/{print $2}'|xargs rm -rf
0 голосов
/ 24 сентября 2010

Использовать подстановку команд с помощью обратных тиков: http://tldp.org/LDP/abs/html/commandsub.html

В вашем случае:

rm -f `svn st | grep ^? | awk '{print $2'}`
0 голосов
/ 24 сентября 2010

Есть несколько способов сделать это с различными недостатками, связанными ... Один из самых простых - использовать цикл while read... Другой способ - использовать цикл for и изменить IFS на \n, но это немного страшнее без особой выгоды. Вы можете увидеть это, хотя, особенно если они хотят разделить слова по пробелам.

git status | while read line; do
    echo "I'm an albatross! $line"
done

или, используя подстановку процесса (которая избавляется от подоболочки, которая может добавить неприятных осложнений )

while read line; do
    echo "I'm an albatross! $line"
done <(git status)

Есть предостережения, которые могут усложнить ситуацию в определенных ситуациях. Вы должны прочитать help read, а FAQ по Bash # 1 содержит множество кровавых подробностей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...