Como unir várias linhas de nomes de arquivos em um com delimitador personalizado?


441

Gostaria de juntar o resultado de ls -1em uma linha e delimitá-lo com o que eu quiser.

Existem comandos Linux padrão que posso usar para conseguir isso?

Respostas:


689

Semelhante à primeira opção, mas omite o delimitador à direita

ls -1 | paste -sd "," -

29
Apenas como uma observação, a versão da pasta que eu tentei requer um argumento "-" no final para dizer para ler em STDIN. eg ls -1 | paste -s -d ":" - Não tenho certeza se isso é universal com todas as versões do colar
Andy White

4
este é melhor porque permite delimitador vazio :)
Yura Purbeev

2
Nota pasterecebe -(entrada padrão) como padrão, pelo menos no meu paste (GNU coreutils) 8.22.
fedorqui 'Então, pare de prejudicar'

1
Acabei de votar, este é e agora tem os mesmos votos que a resposta selecionada. ESTA É A RESPOSTA. nenhum delimitador à direita
thebugfinder 14/15

1
O delimitador vazio pode ser especificado usando "\0", então paste -sd "\0" -funcionou para mim!
Brad Parks

378

EDIT : Simplesmente " ls -m " Se você quiser que seu delimitador seja uma vírgula

Ah, o poder e a simplicidade!

ls -1 | tr '\n' ','

Mude a vírgula " , " para o que quiser. Observe que isso inclui uma "vírgula à direita"


46
+1, mas uma versão mais elaborada deve lidar com o último \ n de maneira diferente
mouviciel

5
Se o nome do arquivo contiver um \n, isso também será substituído.
Codaddict

3
@ShreevatsaR: ele quer não acrescentar um "" à direita, eu acredito. dessa forma #ls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
318 Chris

7
@ Chris: você sedpoderia ser um pouco mais eficiente com o caractere de marcador final:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
pieman72

2
Usar seddepois trparece apenas remover o último símbolo não parece razoável. Eu vou comls -1 | tr '\n' ',' | head -c -1
reddot

29

Isso substitui a última vírgula por uma nova linha:

ls -1 | tr '\n' ',' | sed 's/,$/\n/'

ls -m inclui novas linhas no caractere de largura da tela (80º por exemplo).

Principalmente Bash (apenas lsexterno):

saveIFS=$IFS; IFS=$'\n'
files=($(ls -1))
IFS=,
list=${files[*]}
IFS=$saveIFS

Usando readarray(aka mapfile) no Bash 4:

readarray -t files < <(ls -1)
saveIFS=$IFS
IFS=,
list=${files[*]}
IFS=$saveIFS

Obrigado a gniourf_gniourf pelas sugestões.


Isso não cuidará dos arquivos com espaços em branco no nome. Tente este: dir = / tmp / testdir; rm -rf $ dir && mkdir $ dir && cd / $ dir && touch "este é um arquivo" this_is_another_file && ls -1 && files = ($ (ls -1)) && list = $ {files [@] /% / ,} && list = $ {list% *,} && echo $ list
dimir

1
@dimir: Muitas das respostas a esta pergunta sofrem com esse problema. Editei minha resposta para permitir nomes de arquivos com guias ou espaços, mas não novas linhas.
Pausado até novo aviso.

Sua versão do bash também sofre com expansões de nome de caminho. Para construir uma matriz de linhas, por favor, considere usar mapfile(Bash ≥4) como: mapfile -t files < <(ls -1). Não há necessidade de mexer IFS. E é mais curto também.
gniourf_gniourf

E quando você tem sua matriz, você pode usar IFSpara se juntar aos campos: saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS. Ou use outro método se desejar um separador com mais de um caractere.
gniourf_gniourf

1
@gniourf_gniourf: incluí suas sugestões na minha resposta. Obrigado.
Pausado até novo aviso.

24

Eu acho que este é incrível

ls -1 | awk 'ORS=","'

O ORS é o "separador de registros de saída", agora suas linhas serão unidas por uma vírgula.


6
Isso não exclui o delimitador à direita.
Derek Mahar

6
Isto é especialmente incrível devido ao manuseio separadores de registro de vários caracteres (por exemplo, " OR ")
Mat Schaffer

15

A combinação de configuração IFSe uso de "$*"pode fazer o que você deseja. Estou usando um subshell para não interferir no $ IFS deste shell

(set -- *; IFS=,; echo "$*")

Para capturar a saída,

output=$(set -- *; IFS=,; echo "$*")

2
Você tem mais algumas informações sobre como setfunciona? Parece um pouco de vodu para mim. Um olhar superficial man settambém não me deu muita informação.
Ehtesh Choudhury

3
Se você fornecer setvários argumentos, mas nenhuma opção, ele definirá os parâmetros posicionais ($ 1, $ 2, ...). --existe para proteger setno caso de o primeiro argumento (ou nome do arquivo neste caso) começar com um traço. Veja a descrição da --opção em help set. Acho os parâmetros posicionais uma maneira conveniente de lidar com uma lista de coisas. Eu também poderia ter implementado este com uma matriz:output=$( files=(*); IFS=,; echo "${files[*]}" )
Glenn Jackman

Isso é ótimo, pois não requer a execução de nenhum programa adicional e funciona com nomes de arquivos que contêm espaços ou até novas linhas.
Eric

1
@EhteshChoudhury Como type setvou lhe dizer set is a shell builtin,. Então, man setnão vai ajudar, mas help setvai ajudar. Resposta: "- Atribua os argumentos restantes aos parâmetros posicionais."
Stéphane Gourichon 30/03/16

Depois de um set -- *. Retardando a expansão de *um nível você pode obter a saída correta, sem a necessidade de um sub shell: IFS=',' eval echo '"$*"'. Claro que isso mudará os parâmetros posicionais.
Isaac


9

Não reinvente a roda.

ls -m

Faz exatamente isso.


O OP queria qualquer delimitador para que você ainda precisasse de um tr para converter as vírgulas. Ele também adiciona um espaço após a vírgula ou seja file1, file2, file3
Rob

portanto, usando ls -me trpara remover o espaço após a vírgula que você farials -m | tr -d ' '
Andy

2
esse uso de tr excluirá espaços dentro de nomes de arquivos. melhor usarsed 's/, /,/g
Glenn Jackman

7

apenas bater

mystring=$(printf "%s|" *)
echo ${mystring%|}

5
Um pouco mais eficiente seria usar "printf -v mystring"% s | "*" - que evita uma bifurcação para o $ ()
camh

Mas notavelmente não |mastiga a trilha, @camh.
Christopher

1
Bem, só bashe gnu coreutilsprintf
ThorSummoner

@camh Mas printf -vfuncionará apenas no bash, enquanto a resposta apresentada funciona em muitos tipos de shell.
Isaac

@Christopher Sim, que irá remover a fuga |, desde que ambas as linhas são utilizadas: printf -v mystring "%s|" * ; echo ${mystring%|}.
Isaac

7

Este comando é para os fãs do PERL:

ls -1 | perl -l40pe0

Aqui 40 é o código ASCII octal para o espaço.

-p processará linha por linha e imprimirá

-l cuidará de substituir o \ n final pelo caractere ascii que fornecemos.

-e é informar PERL que estamos executando a linha de comando.

0 significa que não há realmente nenhum comando para executar.

perl -e0 é o mesmo que perl -e ''


6

Para evitar possíveis confusões de nova linha para tr, podemos adicionar o sinalizador -b a ls:

ls -1b | tr '\n' ';'


5

Adicionando além da resposta de majkinetor, aqui está a maneira de remover o delimitador à direita (já que ainda não posso comentar com a resposta dele):

ls -1 | awk 'ORS=","' | head -c -1

Apenas remova quantos bytes à direita o seu delimitador conta.

Eu gosto dessa abordagem porque posso usar delimitadores de vários caracteres + outros benefícios de awk:

ls -1 | awk 'ORS=", "' | head -c -2

EDITAR

Como Peter notou, a contagem de bytes negativos não é suportada na versão nativa do MacOS do cabeçalho. No entanto, isso pode ser facilmente corrigido.

Primeiro instale coreutils. "Os GNU Core Utilities são os utilitários básicos de arquivo, shell e manipulação de texto do sistema operacional GNU."

brew install coreutils

Os comandos também fornecidos pelo MacOS são instalados com o prefixo "g". Por exemplo gls.

Depois de fazer isso, você pode usar gheadum número de bytes negativo, ou melhor, criar um alias:

alias head="ghead"

Nota: contagens negativas de bytes são suportadas apenas em determinadas versões do cabeçote, portanto, isso não funcionará, por exemplo, em macos.
Peter Peter

Obrigado por apontar isso. Eu adicionei uma solução alternativa para o MacOS.
Aleksander Stelmaczonek 6/02/19

4

O caminho sed,

sed -e ':a; N; $!ba; s/\n/,/g'
  # :a         # label called 'a'
  # N          # append next line into Pattern Space (see info sed)
  # $!ba       # if it's the last line ($) do not (!) jump to (b) label :a (a) - break loop
  # s/\n/,/g   # any substitution you want

Nota :

Isso é linear em complexidade, substituindo apenas uma vez depois que todas as linhas são anexadas ao Espaço Padrão do sed.

A resposta de @ AnandRajaseka , e algumas outras respostas semelhantes, como aqui , são O (n²), porque o sed precisa substituir sempre que uma nova linha é anexada ao Espaço Padrão.

Comparar,

seq 1 100000 | sed ':a; N; $!ba; s/\n/,/g' | head -c 80
  # linear, in less than 0.1s
seq 1 100000 | sed ':a; /$/N; s/\n/,/; ta' | head -c 80
  # quadratic, hung

3

Se a sua versão do xargs suportar o sinalizador -d, isso funcionará

ls  | xargs -d, -L 1 echo

-d é o sinalizador delimitador

Se você não possui -d, tente o seguinte

ls | xargs -I {} echo {}, | xargs echo

O primeiro xargs permite que você especifique seu delimitador, que é uma vírgula neste exemplo.


3
-despecifica o delimitador de entrada com GNU xargs, portanto não funcionará. O segundo exemplo exibe o mesmo problema que outras soluções aqui de um delimitador perdido no final.
Thor

3
sed -e :a -e '/$/N; s/\n/\\n/; ta' [filename]

Explicação:

-e- indica um comando a ser executado
:a- é um rótulo
/$/N- define o escopo da correspondência para a atual e a linha ext (N)
s/\n/\\n/;- substitui todos os EOL por \n
ta;- goto rótulo a se a correspondência for bem-sucedida

Retirado do meu blog .



2

lsproduz uma saída de coluna quando conectado a um tubo, portanto -1é redundante.

Aqui está outra resposta perl usando a joinfunção embutida que não deixa um delimitador à direita:

ls | perl -F'\n' -0777 -anE 'say join ",", @F'

O obscuro -0777faz com que o perl leia toda a entrada antes de executar o programa.

alternativa sed que não deixa um delimitador à direita

ls | sed '$!s/$/,/' | tr -d '\n'

0

lstem a opção -mde delimitar a saída com ", "uma vírgula e um espaço.

ls -m | tr -d ' ' | tr ',' ';'

canalizar esse resultado para trremover o espaço ou a vírgula permitirá canalizar o resultado novamente trpara substituir o delimitador.

no meu exemplo, substituo o delimitador ,pelo delimitador;

substitua ;por qualquer delimitador de um caractere que você preferir, pois tr considera apenas o primeiro caractere nas cadeias de caracteres que você transmite como argumentos.


0

Você pode usar chomp para mesclar várias linhas em uma única linha:

perl -e 'while (<>) {if (/ \ $ /) {chomp; } print;} 'bad0> test

Coloque a condição de quebra de linha na instrução if.Pode ser um caractere especial ou qualquer delimitador.


0

Versão Quick Perl com manipulação de barra à direita:

ls -1 | perl -E 'say join ", ", map {chomp; $_} <>'

Explicar:

  • perl -E: executa o Perl com suporte a recursos (digamos, ...)
  • digamos: imprima com retorno da transportadora
  • join ",", ARRAY_HERE: junte uma matriz com ","
  • mapa {chomp; $ _} ROWS: remova de cada linha que a transportadora retorna e retorna o resultado
  • <>: stdin, cada linha é uma ROW, acoplada a um mapa, ela criará uma matriz de cada ROW
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.