Bash while loop lido da lista de caminhos delimitados por dois pontos usando o IFS

4

Estou tentando gravar uma função bash que se comporta de maneira semelhante à where incorporada em tcsh . Em tcsh , where lista todos os builtins, aliases e os caminhos absolutos para executáveis no PATH com um determinado nome, mesmo que sejam sombreados, por exemplo,

tcsh> where tcsh
/usr/bin/tcsh
/bin/tcsh

Como parte disso, quero fazer um loop sobre tudo no $PATH e ver se existe um arquivo executável com o nome apropriado.

O snippet de bash a seguir tem a finalidade de fazer loop por uma lista de caminhos delimitados por dois pontos e imprimir cada componente seguido por uma nova linha, no entanto, parece imprimir todo o conteúdo de $PATH all em uma linha

#!/bin/bash
while IFS=':' read -r line; do
    printf "%s\n" "$line"
done <<< "$PATH"

Como está agora, bash where e ./where apenas imprimem /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

Então, como configuro meu loop while para que o valor da variável de loop seja cada segmento da lista de caminhos separados por dois pontos, por sua vez?

    
por Gregory Nisbet 15.04.2016 / 05:25

5 respostas

6

read usa IFS para separar as palavras na linha que lê, ele não diz read para ler até a primeira ocorrência de qualquer um dos caracteres nele.

IFS=: read -r a b

Gostaria de ler uma linha, colocar a parte antes do primeiro : em $a e o restante em $b .

IFS=: read -r a

colocaria a linha inteira (o restante) em $a (exceto se essa linha contiver apenas um : e for o último caractere na linha).

Se você quiser ler até o primeiro : , use read -d: ( ksh93 , zsh ou bash apenas).

printf %s "$PATH" | while IFS= read -rd: dir || [ -n "$dir" ]; do
  ...
done

(não estamos usando <<< , pois adiciona um caractere extra de nova linha).

Ou você pode usar a divisão de palavras padrão:

IFS=:; set -o noglob
for dir in $PATH""; do
  ...
done

Agora, cuidado com algumas ressalvas:

  • Um componente vazio $PATH significa o diretório atual.
  • Um $PATH vazio significa o diretório atual (ou seja, $PATH contém um componente que é o diretório atual, portanto, o loop while read -d: estaria errado nesse caso).
  • //file não é necessário o mesmo que /file em algum sistema, portanto, se $PATH contiver / , você precisa ter cuidado com itens como $dir/$file .
  • Um unset $ PATH significa que um caminho de busca padrão deve ser usado, não é o mesmo que um conjunto, mas vazio $ PATH.

Agora, se for apenas o equivalente ao comando tcsh / zsh where , você pode usar bash ' type -a .

Mais leitura:

por 15.04.2016 / 06:00
2

Não use loops de shell para processar texto.

Em vez disso, use awk , ou tr ou mesmo sed .

printf %s\n "$PATH" | tr ':' '\n'

printf %s "$PATH" | awk 'BEGIN {RS=":"}; 1'

Ou, como essa é uma variável shell que você está processando, use apenas a substituição bash pattern:

echo "${PATH//:/
}"

(Veja LESS=+/parameter/pattern man bash .)

    
por 15.04.2016 / 05:39
-1

Utilitários da Shell:

echo $PATH | tr ':' '\n'
    
por 15.04.2016 / 05:44
-1

Pure bash , é a mesma coisa que o segundo método do Wildcard anteriormente, mas isso está em uma linha:

echo -e "${PATH//:/"\n"}"
    
por 15.04.2016 / 06:05
-1

Este script funciona de maneira semelhante ao comando where

#!/bin/bash
fn="foo"
for i in 'echo $PATH|tr ':' '\n''
do
  if [[ -e "$i/$fn" ]] ; then
    echo $i
  fi
done

$fn é algo que você deseja encontrar.

$i/$fn pode ser $i$fn às vezes.

    
por 15.04.2016 / 06:05