list=`ls -a R*`
echo $list
Dentro de um script de shell, este comando echo listará todos os arquivos do diretório atual começando com R, mas em uma linha. Como posso imprimir cada item em uma linha?
Eu preciso de um comando genérico para todos os cenários acontecendo com ls
, du
, find -type -d
, etc.
Respostas:
Se a saída do comando contiver várias linhas, cite suas variáveis para preservar essas novas linhas ao ecoar:
Caso contrário, o shell expandirá e dividirá o conteúdo da variável em espaço em branco (espaços, novas linhas, guias) e esses serão perdidos.
fonte
Em vez de colocar uma
ls
saída imprudentemente em uma variável e depoisecho
inseri-la, o que remove todas as cores, useDe
man ls
Eu não aconselho que você faça nada com a saída de
ls
, exceto exibi-lo :)Use, por exemplo, globs de shell e um
for
loop para fazer algo com arquivos ...* isso não inclui o diretório atual
.
ou seu pai,..
emborafonte
echo "$i"
porprintf "%s\n" "$i"
(aquele não bloqueará se um nome de arquivo começar com um "-"). Na verdade, na maioria das vezesecho something
deve ser substituído porprintf "%s\n" "something"
.R
aqui, mas em geral sim. No entanto, mesmo para scripts, tentar sempre acomodar a liderança-
nos caminhos causa problemas: as pessoas assumem--
, que apenas alguns comandos suportam, significa o fim das opções. Eu vifind . [tests] -exec [cmd] -- {} \;
onde[cmd]
não suporta--
. Todo caminho começa com.
qualquer maneira! Mas isso não é argumento contra a substituiçãoecho
comprintf '%s\n'
. Aqui, pode-se substituir o loop inteiro porprintf '%s\n' R/*
. O Bash temprintf
como interno, portanto, não há efetivamente nenhum limite para o número / comprimento dos argumentos.Embora colocá-lo entre aspas, como o @muru sugeriu , de fato fará o que você pediu, você também pode considerar o uso de uma matriz para isso. Por exemplo:
O
IFS=$'\n'
comando diz ao bash para dividir apenas a saída em caracteres de nova linha para obter cada elemento da matriz. Sem ele, ele será dividido em espaços, portanto,a file name with spaces.txt
seriam 5 elementos separados em vez de um. Essa abordagem será interrompida se os nomes de arquivos / diretórios puderem conter novas linhas (\n
). Ele salvará cada linha da saída do comando como um elemento da matriz.Observe que eu também mudei o estilo antigo
`command`
para o$(command)
qual é a sintaxe preferida.Agora você tem uma matriz chamada
$dirs
, cada bof cujos elementos são uma linha da saída do comando anterior. Por exemplo:Agora, devido a alguma estranheza do bash (veja aqui ), você precisará redefinir
IFS
o valor original depois de fazer isso. Portanto, salve-o e reasigne:Ou faça isso manualmente:
Como alternativa, basta fechar o terminal atual. O seu novo terá o IFS original definido novamente.
fonte
du
faz parecer OP está mais interessada em preservar o formato de saída.Se você precisar armazenar a saída e desejar uma matriz,
mapfile
será mais fácil.Primeiro, considere se você precisa armazenar a saída do seu comando. Caso contrário, basta executar o comando
Se você decidir ler a saída de um comando como uma matriz de linhas, é verdade que uma maneira de fazê-lo é desativar o globbing, definir
IFS
a divisão em linhas, usar a substituição de comando dentro da(
)
sintaxe de criação da matriz e redefinirIFS
depois, o que é mais complexo do que parece. A resposta de Terdon cobre parte dessa abordagem. Mas eu sugiro que você usemapfile
, o shell Bash é built-in comando para leitura de texto como um conjunto de linhas, em vez disso. Aqui está um caso simples em que você está lendo de um arquivo:Isso lê linhas em uma matriz chamada
MAPFILE
. Sem o redirecionamento de entrada , você estaria lendo a entrada padrão do shell (normalmente o seu terminal) em vez do arquivo nomeado por . Para ler em uma matriz que não seja o padrão , passe seu nome. Por exemplo, isso lê na matriz :< filename
filename
MAPFILE
lines
Outro comportamento padrão que você pode optar por alterar é que os caracteres de nova linha no final das linhas são deixados no lugar; eles aparecem como o último caractere em cada elemento da matriz (a menos que a entrada termine sem um caractere de nova linha; nesse caso, o último elemento não possui um). Para incluir essas novas linhas de forma que elas não apareçam na matriz, passe a
-t
opção paramapfile
. Por exemplo, isso é lido na matrizrecords
e não grava caracteres de nova linha à direita em seus elementos da matriz:Você também pode usar
-t
sem passar um nome de matriz; isto é, ele também funciona com um nome implícito de matrizMAPFILE
.O
mapfile
shell embutido suporta outras opções e, alternativamente, pode ser chamado comoreadarray
. Executehelp mapfile
(ehelp readarray
) para obter detalhes.Mas você não deseja ler de um arquivo, mas da saída de um comando. Para conseguir isso, use a substituição do processo . Este comando lê as linhas do comando
some-command
comarguments...
seus argumentos de linha de comando e as coloca namapfile
matriz padrãoMAPFILE
, com seus caracteres finais de nova linha removidos:A substituição do processo substitui por um nome de arquivo real a partir do qual a saída da execução pode ser lida. O arquivo é um pipe nomeado em vez de um arquivo normal e, no Ubuntu, será nomeado como (às vezes com outro número ), mas você não precisa se preocupar com os detalhes, porque o shell cuida disso tudo nos bastidores.
<(some-command arguments...)
some-command arguments...
/dev/fd/63
63
Você pode pensar que poderia renunciar à substituição do processo usando , mas isso não funcionará porque, quando você tem um pipeline de vários comandos separados por , o Bash executa todos os comandos em subshells . Assim, tanto e executar em seus próprios ambientes , inicializadas a partir, mas separado do ambiente do shell em que você executar o pipeline. No subshell onde é executado, a matriz é preenchida, mas essa matriz é descartada quando o comando termina. nunca é criado ou modificado para o chamador.
some-command arguments... | mapfile -t
|
some-command arguments...
mapfile -t
mapfile -t
MAPFILE
MAPFILE
Aqui está a aparência do exemplo na resposta de terdon , na íntegra, se você usar
mapfile
:É isso aí. Você não precisa verificar se
IFS
foi definido , acompanhar se não foi definido e com qual valor, defini-lo como uma nova linha e redefini-lo ou reconfigurá-lo posteriormente. Você não precisa desativar o globbing (por exemplo, comset -f
) - o que é realmente necessário para usar esse método com seriedade, pois os nomes de arquivos podem conter*
e?
- e[
depois reativá-lo (set +f
) posteriormente.Você também pode substituir esse loop específico inteiramente por um único
printf
comando - embora isso não seja realmente um benefíciomapfile
, pois você pode fazer isso usandomapfile
ou outro método para preencher a matriz. Aqui está uma versão mais curta:É importante ter em mente que, como menciona Terdon , operar linha por linha nem sempre é apropriado e não funcionará corretamente com nomes de arquivos que contêm novas linhas. Eu recomendo nomear arquivos dessa maneira, mas isso pode acontecer, inclusive por acidente.
Não existe realmente uma solução única para todos.
Você pediu "um comando genérico para todos os cenários" e a abordagem de usar
mapfile
um pouco aproxima esse objetivo, mas peço que você reconsidere seus requisitos.A tarefa mostrada acima é melhor alcançada com apenas um único
find
comando:Você também pode usar um comando externo como
sed
adicionarDIR:
ao início de cada linha. É indiscutivelmente um pouco feio e, ao contrário do comando find, ele adicionará "prefixos" extras nos nomes de arquivos que contêm novas linhas, mas funciona independentemente da entrada, portanto atende aos seus requisitos e ainda é preferível ler a saída em um arquivo. variável ou matriz :Se você precisar listar e também executar uma ação em cada diretório encontrado, como executar
some-command
e passar o caminho do diretório como argumento,find
faça isso também:Como outro exemplo, voltemos à tarefa geral de adicionar um prefixo a cada linha. Suponha que eu queira ver a saída de,
help mapfile
mas numere as linhas. Na verdade, eu não usariamapfile
isso nem qualquer outro método que o leia em uma variável de shell ou matriz de shell. Supondohelp mapfile | cat -n
que não dê a formatação que eu quero, eu posso usarawk
:Ler toda a saída de um comando em uma única variável ou matriz às vezes é útil e apropriado, mas possui grandes desvantagens. Você não precisa apenas lidar com a complexidade adicional de usar seu shell quando um comando ou combinação de comandos existentes já pode fazer o que você precisa melhor ou melhor, mas toda a saída do comando deve ser armazenada na memória. Às vezes você pode saber que isso não é um problema, mas às vezes você pode estar processando um arquivo grande.
Uma alternativa comumente tentada - e às vezes feita corretamente - é ler a entrada linha por linha
read -r
em um loop. Se você não precisar armazenar linhas anteriores enquanto estiver operando em linhas posteriores e precisar usar entradas longas, pode ser melhor quemapfile
. Mas também deve ser evitado nos casos em que você pode simplesmente canalizá-lo para um comando que pode fazer o trabalho, que é a maioria dos casos .fonte