Fazendo ações em uma lista a partir do shell

3

Eu tenho uma lista de pacotes que eu quero desinstalar. O programa de desinstalador pkg_deinstall não recebe uma lista de pacotes como parâmetro para desinstalar. Como eu poderia desinstalar da lista (como um loop foreach)?

[root@fbsd01 /usr/ports/editors/vim]# pkg_info | grep proto| sed 's/\([a-z0-9]*\).*//'
bigreqsproto
compositeproto
damageproto
fixesproto
fontsproto
inputproto
kbproto
randrproto
renderproto
xcb
xcmiscproto
xextproto
xf86bigfontproto
xineramaproto

Eu acho que um comando semelhante a este funcionaria, mas eu tenho que passar a lista como um parâmetro e não um fluxo: pkg_info | grep proto| sed 's/\([a-z0-9]*\).*//' | head -n 1 pkg_deinstall

Se você pudesse me dar algumas pistas sobre qual programa e sintaxe usar, isso seria útil. Eu sei desde que é único que você pode ter dificuldade em chegar com uma resposta exata. Se minha pergunta for muito complicada, talvez alguém possa me mostrar como fazer ações em um ls de arquivos em um diretório.

    
por EhevuTov 29.07.2011 / 18:11

3 respostas

5

Para o mais básico 'foreach' existe xargs , ele lê alguns parâmetros e os anexa a alguma outra linha de comando. Por exemplo, você pode tentar:

> cut -d: -f1 /etc/passwd | xargs -n1 echo user:

Observe que você deve passar um argumento -d'\n' para xargs , no caso de nomes de usuário poderem conter caracteres de espaço. Para adicionar todos os parâmetros ao mesmo comando, use:

> cut -d: -f1 /etc/passwd | xargs echo users:

Para loops mais gerais, você pode usar isto:

> cut -d: -f1 /etc/passwd | while read i; do echo $i; done

Desta vez, $i pode ser colocado em qualquer lugar em uma sequência de um ou mais comandos. As entradas são lidas uma linha por vez e armazenadas na variável i . Também é possível dividir² a linha e armazená-la em diversas variáveis:

> cut -d: -f1,6 /etc/passwd --output-delimiter=' ' | \
  while read i j; do echo home directory of user $i is $j; done

Note que para loops sobre nomes de arquivos brutos, o seguinte é melhor (veja os comentários):

> for i in *.txt; do echo file: $i; done

-
1. Mais precisamente: tantos quanto o sistema permitirá.
2. De acordo com a variável IFS

    
por 29.07.2011 / 18:45
3

Existem duas maneiras principais de converter um fluxo de saída em uma lista de argumentos. A maneira rápida (e às vezes suja) de fazer isso é com xargs . Por padrão, ele aceita um fluxo de entrada padrão e executa um comando com cada linha da entrada como um argumento separado. Você deve sempre ter cuidado com o formato da entrada porque os espaços e outros caracteres são fáceis de interpretar incorretamente quando passados como argumentos. Nomes de pacotes, no entanto, são relativamente seguros. Eles não devem incluir caracteres curinga de shell ou novas linhas ou nada de louco, então aqui está o que seria para o seu comando: [3]

$ pkg_info | grep proto | sed 's/\([a-z0-9]*\).*//' | xargs pkg_deinstall -n

Um caso frequente é que você realmente precisa executar o comando uma vez para cada linha sendo processada. Nesse caso, o argumento -n é útil. Você pode usar -n1 para executá-los um de cada vez, ou mesmo -n10 para executá-los em lotes de dez.

$ [pipline] | xargs -n1 pkg_deinstall -n

Agora, digamos que você tenha necessidade de adicionar outro argumento depois de gerado automaticamente uma vez. Nesse caso, você pode usar '-I' para especificar uma string para substituir pelos argumentos gerados automaticamente. Isso também permitirá que você faça coisas como citar o argumento, se necessário. Você pode então formatar onde os argumentos serão colocados assim: [1] [2]

$ [pipline] | xargs -n1 -I{} pkg_deinstall -n "{}" --trailing-argument

Se você quiser mais controle ou executar vários comandos para cada linha de entrada, sua próxima opção é usar um loop como este:

$ [pipline] | while read line; do
      echo "Uninstalling $line..."
      pkg_deinstall -n "$line"
  done

Observe que o pkg_deinstall usa automaticamente a expressão como uma correspondência parcial glob, portanto, tenha cuidado com o que você deseja que seja desinstalado. Passar o nome base de algum componente do sistema irá desinstalar não apenas esse componente, mas tudo o que tem um nome como ele ou depende dele! Isso pode ser catastrófico, portanto, sempre verifique cuidadosamente o conjunto de pacotes. Você pode usar -n para fazer uma execução seca que mostre o que seria feito sem realmente fazer [3] e / ou -i para executar cada etapa de forma interativa.

[1] Eu usei {} porque o find e outras funções exec similares usam esse formato, mas qualquer string única serve.
[2] O pkg_deinstall comando não tem nenhum argumento que viria após o patern, então este é apenas um exemplo!
[3] Eu adicionei -n a todos os meus exemplos para que se você copiar e colar o código não fará nada, apenas mostrará um teste.

    
por 30.07.2011 / 16:22
1

Não tirar as outras respostas ... Essa é apenas mais uma maneira de fazer isso:

for pkg in $( pkg_info | grep proto| sed 's/\([a-z0-9]*\).*//' ) ; do
    pkg_deinstall $pkg
done

As citações são um pesadelo no exemplo, então, pessoalmente, costumo usar | while read foo ... com mais frequência do que não.

    
por 30.07.2011 / 16:37