Jak połączyć wiele wierszy nazw plików w jeden za pomocą niestandardowego separatora?


441

Chciałbym połączyć wynik ls -1w jedną linię i wytyczyć to, co chcę.

Czy są jakieś standardowe polecenia Linuksa, których mogę użyć, aby to osiągnąć?

Odpowiedzi:


689

Podobne do pierwszej opcji, ale pomija ogranicznik końcowy

ls -1 | paste -sd "," -

29
Dla przypomnienia wersja wklejonej przeze mnie wersji wymaga argumentu „-” na końcu, aby nakazać jej odczytanie ze STDIN. np. ls -1 | paste -s -d ":" - Nie jestem pewien, czy jest to uniwersalne dla wszystkich wersji pasty
Andy White

4
ten jest lepszy, ponieważ pozwala na pusty ogranicznik :)
Yura Purbeev

2
Uwaga pastedostaje -(standardowe wejście) domyślnie, przynajmniej na mój paste (GNU coreutils) 8.22.
fedorqui „SO przestań krzywdzić”

1
właśnie go głosowałem, to jest i teraz ma tyle samo głosów co wybrana odpowiedź. TO ODPOWIEDŹ. brak ogranicznika końcowego
thebugfinder

1
Pusty separator można określić za pomocą "\0", więc paste -sd "\0" -działało dla mnie!
Brad Parks,

379

EDYCJA : Po prostu „ ls -m ” Jeśli chcesz, aby separator był przecinkiem

Ach, siła i prostota!

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

Zmień przecinek „ , ” na cokolwiek chcesz. Pamiętaj, że obejmuje to „przecinek końcowy”


46
+1, ale bardziej rozbudowana wersja powinna traktować ostatnią inaczej \ n
mouviciel

5
Jeśli nazwa pliku zawiera \nw sobie, to również to zastąpi.
codaddict

3
@ShreevatsaR: on nie chce dołączać końcowego ”,„ wierzę. takls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
Chris

7
@Chris: możesz sedbyć trochę bardziej wydajny dzięki postaci ze znacznikiem końcowym:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
pieman72,

2
Używanie sedpo trwydaje się po prostu usunąć ostatni symbol wydaje się nieuzasadnione. Idę zls -1 | tr '\n' ',' | head -c -1
reddot

29

To zastępuje ostatni przecinek nową linią:

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

ls -m zawiera znaki nowej linii na znaku szerokości ekranu (na przykład 80.).

Głównie Bash (tylko lszewnętrzny):

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

Używanie readarray(aka mapfile) w Bash 4:

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

Dzięki gniourf_gniourf za sugestie.


To nie zajmie się plikami z białymi spacjami w nazwie. Spróbuj tego: dir = / tmp / testdir; rm -rf $ katalog i & mkdir $ katalog i & cd / $ katalog i & touch "to jest plik" ten_ kolejny_plik i & ls -1 && files = ($ (ls -1)) && list = $ {pliki [@] /% / ,} && list = $ {list% *,} && echo $ list
dimir

1
@dimir: Wiele odpowiedzi na to pytanie cierpi z powodu tego problemu. Zredagowałem swoją odpowiedź, aby zezwolić na nazwy plików zawierające tabulatory lub spacje, ale nie znaki nowej linii.
Wstrzymano do odwołania.

Twoja wersja bash również cierpi z powodu rozszerzenia nazw ścieżek. Aby zbudować tablicę z liniami należy rozważyć zastosowanie mapfile(Bash ≥ 4) jako: mapfile -t files < <(ls -1). Nie musisz się bawić IFS. I jest też krótszy.
gniourf_gniourf

A gdy masz tablicę można wykorzystać IFSdo przyłączenia się do pola: saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS. Lub użyj innej metody, jeśli chcesz separator zawierający więcej niż jeden znak.
gniourf_gniourf

1
@gniourf_gniourf: Dołączyłem twoje sugestie do mojej odpowiedzi. Dzięki.
Wstrzymano do odwołania.

24

Myślę, że ten jest niesamowity

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

ORS jest „separatorem rekordów wyjściowych”, więc teraz linie zostaną połączone przecinkiem.


6
Nie wyklucza to ogranicznika końcowego.
Derek Mahar

6
Jest to szczególnie niesamowite ze względu na obsługę wieloznakowych separatorów rekordów (np. " OR ")
Mat Schaffer,

15

Połączenie ustawienia IFSi użycia "$*"może robić, co chcesz. Używam podpowłoki, więc nie przeszkadzam w $ IFS tej powłoki

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

Aby uchwycić wynik,

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

2
Czy masz więcej informacji na temat tego, jak setdziała? Dla mnie wygląda to trochę jak voodoo. płytki przegląd man setnie dostarczył mi również wielu informacji.
Ehtesh Choudhury,

3
Jeśli podasz setkilka argumentów, ale nie masz opcji, ustawia parametry pozycyjne (1 $, 2 $, ...). --jest do ochrony setna wypadek, gdyby pierwszy argument (lub nazwa pliku w tym przypadku) zaczął się od myślnika. Zobacz opis --opcji w help set. Uważam, że parametry pozycyjne są wygodnym sposobem obsługi listy rzeczy. Mógłbym również zaimplementować to z tablicą:output=$( files=(*); IFS=,; echo "${files[*]}" )
glenn jackman

Jest to świetne, ponieważ nie wymaga wykonywania żadnych dodatkowych programów i działa z nazwami plików, które zawierają spacje, a nawet znaki nowej linii.
Eric

1
@EhteshChoudhury Jak type setpowie, set is a shell builtin. Więc man setnie pomoże, ale help setzrobi. Odpowiedź: „- Przypisz pozostałe argumenty do parametrów pozycyjnych.”
Stéphane Gourichon

Po set -- *. Opóźniając ekspansję *jednego poziomu można uzyskać poprawny wynik bez konieczności powłoki sub: IFS=',' eval echo '"$*"'. Oczywiście to zmieni parametry pozycyjne.
Izaak


9

Nie wymyślaj koła ponownie.

ls -m

Robi dokładnie to.


OP chciał dowolnego separatora, więc nadal będziesz potrzebował tr, aby przekonwertować przecinki. Dodaje również spację po przecinkach, tj. Plik1, plik2, plik3
rob

więc używając ls -mi, traby usunąć spację po przecinku, zrobiłbyśls -m | tr -d ' '
Andy

2
użycie tr usunie spacje w nazwach plików. lepiej użyćsed 's/, /,/g
glenn jackman

7

po prostu bash

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

5
Nieco bardziej wydajne byłoby użycie „printf -v mystring”% s | "*” - co pozwala uniknąć rozwidlenia $ ()
camh

Ale w szczególności nie |pogryza końcowego , @camh.
Christopher

1
Cóż, just bashi gnu coreutilsprintf
ThorSummoner

@camh Ale printf -vbędzie działać tylko w trybie bash, podczas gdy przedstawiona odpowiedź działa na wielu typach powłok.
Izaak

@Christopher Tak, że usunie końcowego znaku |, pod warunkiem, że obie linie są używane: printf -v mystring "%s|" * ; echo ${mystring%|}.
Izaak

7

To polecenie jest przeznaczone dla fanów PERL:

ls -1 | perl -l40pe0

Tutaj 40 to ósemkowy kod ascii dla przestrzeni.

-p przetworzy wiersz po wierszu i wydrukuje

-l zajmie się zastąpieniem końcowego znaku \ n przez zapewniany przez nas znak ascii.

-e ma poinformować PERL, że wykonujemy wykonanie wiersza poleceń.

0 oznacza, że ​​tak naprawdę nie ma polecenia do wykonania.

perl -e0 jest taki sam jak perl -e ''


6

Aby uniknąć potencjalnego pomyłki nowego wiersza dla tr, możemy dodać flagę -b do ls:

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

5

Wygląda na to, że odpowiedzi już istnieją.

Jeśli chcesz a, b, cformatować, użyj ls -m(odpowiedź Tulainsa Córdova )

Lub jeśli chcesz a b cformatować, użyj ls | xargs(uproszczonej wersji odpowiedzi Chrisa J. )

Lub jeśli chcesz jakikolwiek inny ogranicznik |, użyj ls | paste -sd'|'(zastosowanie odpowiedzi Artem )


5

Dodając do odpowiedzi majkinetor, oto sposób usuwania ogranicznika końcowego (ponieważ nie mogę jeszcze skomentować pod jego odpowiedzią):

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

Po prostu usuń tyle bajtów końcowych, ile liczy Twój separator.

Podoba mi się to podejście, ponieważ mogę używać ograniczników wieloznakowych + innych korzyści awk:

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

EDYTOWAĆ

Jak zauważył Peter, ujemna liczba bajtów nie jest obsługiwana w natywnej wersji głowy MacOS. Można to jednak łatwo naprawić.

Najpierw zainstaluj coreutils. „Narzędzia GNU Core to podstawowe narzędzia do obsługi plików, powłok i tekstów w systemie operacyjnym GNU.”

brew install coreutils

Polecenia również dostarczane przez MacOS są instalowane z prefiksem „g”. Na przykładgls .

Gdy to zrobisz, możesz użyć, gheadktóry ma ujemną liczbę bajtów lub lepiej, zrobić alias:

alias head="ghead"

Uwaga: ujemne liczby bajtów są obsługiwane tylko w niektórych wersjach head, więc to nie będzie działać np. W macos.
Peter

Dzięki za wskazanie tego. Dodałem obejście dla MacOS.
Aleksander Stelmaczonek

4

Sed sposób

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

Uwaga :

Jest to złożoność liniowa, zastępująca tylko raz po dodaniu wszystkich linii do Przestrzeni Wzorów sed.

@ Odpowiedź AnandRajaseki i inne podobne odpowiedzi, takie jak tutaj , to O (n²), ponieważ sed musi zastępować za każdym razem, gdy do Przestrzeni Wzorów dodawana jest nowa linia.

Porównywać,

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

Jeśli twoja wersja xargs obsługuje flagę -d, to powinno działać

ls  | xargs -d, -L 1 echo

-d jest flagą separatora

Jeśli nie masz -d, możesz wypróbować następujące

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

Pierwsze xargs pozwala określić ogranicznik, który w tym przykładzie jest przecinkiem.


3
-dokreśla separator wejściowy z GNU xargs, więc nie będzie działać. Drugi przykład pokazuje ten sam problem, co inne rozwiązania ogranicznika błądzenia na końcu.
Thor

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

Wyjaśnienie:

-e- oznacza polecenie do wykonania
:a- jest etykietą
/$/N- określa zakres dopasowania dla bieżącej i linii (N) ext
s/\n/\\n/;- zamienia wszystkie EOL na\n
ta; - goto label a, jeśli dopasowanie się powiedzie

Zaczerpnięte z mojego bloga .



2

ls tworzy jedną kolumnę wyjściową po podłączeniu do rury, więc -1 jest nadmiarowa.

Oto kolejna odpowiedź Perla, wykorzystująca wbudowaną joinfunkcję, która nie pozostawia ogranicznika końcowego:

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

Niejasne -0777zmusza Perla do odczytania wszystkich danych wejściowych przed uruchomieniem programu.

sed alternatywa, która nie pozostawia ogranicznika końcowego

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

0

lsma opcję -mograniczenia danych wyjściowych ", "przecinkiem i spacją.

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

potokowanie tego wyniku w trcelu usunięcia spacji lub przecinka pozwoli na ponowne potokowanie wyniku w trcelu zastąpienia ogranicznika.

w moim przykładzie zastępuję separator ,separatorem;

zamień na ;dowolny ogranicznik jednego znaku , ponieważ tr uwzględnia tylko pierwszy znak w ciągach, które przekazujesz jako argumenty.


0

Możesz użyć chomp, aby scalić wiele linii w jedną linię:

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

wstaw warunek przerwania linii w instrukcji if. Może to być znak specjalny lub dowolny separator.


0

Wersja Quick Perl z końcowym ukośnikiem:

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

Wytłumaczyć:

  • perl -E: uruchom Perla z obsługą funkcji (powiedzmy ...)
  • powiedz: wydrukuj ze zwrotem przewoźnika
  • join ”,”, ARRAY_HERE: dołącz do tablicy za pomocą „,”
  • mapa {chomp; $ _} ROWS: usuń z każdej linii zwrot przewoźnika i zwróć wynik
  • <>: stdin, każda linia to ROW, łącząc się z mapą utworzy tablicę każdego ROW
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.