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.