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ąć?

JavaRocky
źródło

Odpowiedzi:

689

Podobne do pierwszej opcji, ale pomija ogranicznik końcowy

ls -1 | paste -sd "," -
Artem
źródło
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”

zaf
źródło
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.

Wstrzymano do odwołania.
źródło
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.

majkinetor
źródło
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 "$*")
Glenn Jackman
źródło
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.

Tulains Córdova
źródło
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%|}
ghostdog74
źródło
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 ''

Sidharth C. Nadhan
źródło
6

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

ls -1b | tr '\n' ';'
yabt
źródło
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 )

plhn
źródło
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"
Aleksander Stelmaczonek
źródło
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
zhazha
źródło
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.

Chris J
źródło
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 .

Anand Rajasekar
źródło
2

Możesz użyć:

ls -1 | perl -pe 's/\n$/some_delimiter/'
kodaddict
źródło
Nie wyklucza to ogranicznika końcowego.
Derek Mahar
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'
Thor
źródło
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.

Andy
źródło
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.

Suman
źródło
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
Celogeek San
źródło