Làm cách nào để nối nhiều dòng tên tệp thành một với dấu phân cách tùy chỉnh?


441

Tôi muốn tham gia kết quả của ls -1một dòng và phân định nó với bất cứ điều gì tôi muốn.

Có bất kỳ lệnh Linux tiêu chuẩn nào tôi có thể sử dụng để đạt được điều này không?

Câu trả lời:


689

Tương tự như tùy chọn đầu tiên nhưng bỏ qua dấu phân cách

ls -1 | paste -sd "," -

29
Cũng giống như một ghi chú, phiên bản dán mà tôi đã thử yêu cầu một đối số "-" ở cuối để bảo nó đọc từ STDIN. ví dụ: ls -1 | paste -s -d ":" - Không chắc chắn nếu đó là phổ quát với tất cả các phiên bản dán
Andy White

4
cái này tốt hơn vì nó cho phép dấu phân cách trống :)
Yura Purbeev

2
Lưu ý pasteđược -(đầu vào tiêu chuẩn) như mặc định, ít nhất là trên của tôi paste (GNU coreutils) 8.22.
fedorqui 'SO ngừng làm hại'

1
Tôi chỉ nâng cấp nó, đây là và bây giờ nó có cùng số phiếu với câu trả lời được chọn. ĐÂY LÀ CÂU TRẢ LỜI. không có dấu vết đo
bọ rùa

1
Dấu phân cách trống có thể được chỉ định bằng cách sử dụng "\0", vì vậy paste -sd "\0" -làm việc cho tôi!
Brad

378

EDIT : Đơn giản là " ls -m " Nếu bạn muốn dấu phân cách của bạn là dấu phẩy

Ah, sức mạnh và sự đơn giản!

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

Thay đổi dấu phẩy " , " thành bất cứ điều gì bạn muốn. Lưu ý rằng điều này bao gồm một "dấu phẩy"


46
+1, nhưng một phiên bản phức tạp hơn sẽ xử lý khác nhau lần cuối
mouviciel

5
Nếu tên tệp chứa một \ntrong đó, điều này cũng sẽ thay thế.
codaddict

3
@ShreevatsaR: anh ta có nghĩa là không nối đuôi "," tôi tin. như vậyls -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
Chris

7
@Chris: bạn sedcó thể hiệu quả hơn một chút với ký tự đánh dấu kết thúc:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
pieman72 17/12/13

2
Sử dụng sedsau trdường như chỉ để loại bỏ biểu tượng cuối cùng có vẻ không hợp lý. Tôi đi vớils -1 | tr '\n' ',' | head -c -1
reddot

29

Điều này thay thế dấu phẩy cuối cùng bằng một dòng mới:

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

ls -m bao gồm các dòng mới ở ký tự chiều rộng màn hình (ví dụ thứ 80).

Chủ yếu là Bash (chỉ lslà bên ngoài):

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

Sử dụng readarray(aka mapfile) trong Bash 4:

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

Cảm ơn gniourf_gniourf cho các đề xuất.


Điều này sẽ không chăm sóc các tệp có khoảng trắng trong tên. Hãy thử cái này: dir = / tmp / testdir; rm -rf $ dir && mkdir $ dir && cd / $ dir && touch "đây là một tệp" this_is_another_file && ls -1 && files = ($ (ls -1)) && list = $ {files [@] /% / ,} && list = $ {list% *,} && echo $ list
dimir

1
@dimir: Nhiều câu trả lời cho câu hỏi này bị vấn đề này. Tôi đã chỉnh sửa câu trả lời của mình để cho phép tên tệp có tab hoặc dấu cách, nhưng không phải dòng mới.
Tạm dừng cho đến khi có thông báo mới.

Phiên bản bash của bạn cũng bị mở rộng tên đường dẫn. Để xây dựng một mảng từ các dòng, vui lòng xem xét sử dụng mapfile(Bash 4) là : mapfile -t files < <(ls -1). Không cần phải mân mê với IFS. Và nó cũng ngắn hơn.
gniourf_gniourf

Và khi bạn có mảng của mình, bạn có thể sử dụng IFSđể tham gia các trường : saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS. Hoặc sử dụng một phương thức khác nếu bạn muốn một dấu phân cách có nhiều ký tự đó.
gniourf_gniourf

1
@gniourf_gniourf: Tôi đã bao gồm các đề xuất của bạn trong câu trả lời của tôi. Cảm ơn.
Tạm dừng cho đến khi có thông báo mới.

24

Tôi nghĩ rằng điều này là tuyệt vời

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

ORS là "dấu tách bản ghi đầu ra", vì vậy bây giờ các dòng của bạn sẽ được nối bằng dấu phẩy.


6
Điều này không loại trừ dấu phân cách.
Derek Mahar

6
Điều này đặc biệt tuyệt vời do xử lý các dấu tách bản ghi nhiều ký tự (ví dụ " OR ":)
Mat Schaffer

15

Sự kết hợp của thiết lập IFSvà sử dụng "$*"có thể làm những gì bạn muốn. Tôi đang sử dụng một lớp con nên tôi không can thiệp vào $ IFS của shell này

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

Để nắm bắt đầu ra,

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

2
Bạn có thêm một số thông tin liên quan đến cách làm setviệc? Trông hơi giống voodoo với tôi. Nhìn nông cạn man setcũng không cho tôi nhiều thông tin.
Ehtesh Choudhury

3
Nếu bạn đưa ra setmột loạt các đối số nhưng không có tùy chọn, nó sẽ đặt các tham số vị trí ($ 1, $ 2, ...). --có để bảo vệ settrong trường hợp đối số đầu tiên (hoặc tên tệp trong trường hợp này) xảy ra để bắt đầu bằng dấu gạch ngang. Xem mô tả của --tùy chọn trong help set. Tôi tìm thấy các tham số vị trí một cách thuận tiện để xử lý một danh sách các thứ. Tôi cũng có thể đã thực hiện điều này với một mảng:output=$( files=(*); IFS=,; echo "${files[*]}" )
glenn jackman

Điều này thật tuyệt vì nó không yêu cầu thực hiện bất kỳ chương trình bổ sung nào và nó hoạt động với tên tệp có chứa khoảng trắng hoặc thậm chí là dòng mới.
Eric

1
@EhteshChoudhury Như type setsẽ nói với bạn , set is a shell builtin. Vì vậy, man setsẽ không giúp đỡ, nhưng help setsẽ làm. Trả lời: "- Gán mọi đối số còn lại cho các tham số vị trí."
Stéphane Gourichon 30/03/2016

Sau một set -- *. Trì hoãn mở rộng *một cấp độ, bạn có thể nhận được đầu ra chính xác mà không cần vỏ phụ : IFS=',' eval echo '"$*"'. Tất nhiên điều đó sẽ thay đổi các tham số vị trí.
Isaac


9

Đừng phát minh lại bánh xe.

ls -m

Nó làm chính xác điều đó.


OP muốn bất kỳ dấu phân cách nào, do đó bạn vẫn cần một tr để chuyển đổi dấu phẩy. Nó cũng thêm một khoảng trắng sau dấu phẩy tức là file1, file2, file3
cướp

vì vậy, sử dụng ls -mtrxóa khoảng trắng sau dấu phẩy bạn sẽ làmls -m | tr -d ' '
Andy

2
việc sử dụng tr sẽ xóa khoảng trắng bên trong tên tệp. tốt hơn để sử dụngsed 's/, /,/g
glenn jackman

7

chỉ cần bash

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

5
Hiệu quả hơn một chút sẽ là sử dụng "printf -v mystring"% s | "*" - để tránh một ngã ba cho $ ()
camh

Nhưng đáng chú ý là không theo dõi dấu vết |, @camh.
Christopher

1
Chà, bashprintf
lõi

@camh Nhưng printf -vsẽ chỉ hoạt động trong bash, trong khi câu trả lời được trình bày hoạt động trên nhiều loại vỏ.
Isaac

@Christopher Có, điều đó sẽ xóa dấu vết |, với điều kiện là cả hai dòng được sử dụng : printf -v mystring "%s|" * ; echo ${mystring%|}.
Isaac

7

Lệnh này dành cho người hâm mộ PERL:

ls -1 | perl -l40pe0

Ở đây 40 là mã bát phân bát phân cho không gian.

-p sẽ xử lý từng dòng và in

-l sẽ đảm nhiệm việc thay thế dấu vết \ n bằng ký tự ascii mà chúng tôi cung cấp.

-e là để thông báo cho PERL chúng tôi đang thực hiện dòng lệnh.

0 có nghĩa là thực sự không có lệnh để thực thi.

perl -e0 giống như perl -e ''


6

Để tránh nhầm lẫn dòng mới tiềm năng cho tr, chúng tôi có thể thêm cờ -b vào ls:

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


5

Thêm vào đầu câu trả lời của Majkinetor, đây là cách xóa dấu phân cách dấu (vì tôi không thể chỉ nhận xét dưới câu trả lời của anh ấy):

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

Chỉ cần loại bỏ bao nhiêu byte theo sau là dấu phân cách của bạn.

Tôi thích cách tiếp cận này vì tôi có thể sử dụng các dấu phân cách nhiều ký tự + các lợi ích khác của awk:

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

BIÊN TẬP

Như Peter đã nhận thấy, số byte âm không được hỗ trợ trong phiên bản đầu MacOS gốc. Điều này tuy nhiên có thể dễ dàng sửa chữa.

Đầu tiên, cài đặt coreutils. "Các tiện ích cốt lõi của GNU là các tiện ích thao tác tệp, vỏ và văn bản cơ bản của hệ điều hành GNU."

brew install coreutils

Các lệnh cũng được cung cấp bởi MacOS được cài đặt với tiền tố "g". Ví dụ gls.

Khi bạn đã hoàn thành việc này, bạn có thể sử dụng gheadcó số byte âm hoặc tốt hơn, tạo bí danh:

alias head="ghead"

Lưu ý: số byte âm chỉ được hỗ trợ trên các phiên bản đầu nhất định, do đó, điều này sẽ không hoạt động trên ví dụ như macos.
Peter

Cảm ơn, đã chỉ ra rằng. Tôi đã thêm một cách giải quyết cho MacOS.
Aleksander Stelmaczonek

4

Cách quyến rũ,

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

Lưu ý :

Đây là tuyến tính phức tạp, chỉ thay thế một lần sau khi tất cả các dòng được nối vào Không gian mẫu của sed.

Câu trả lời của @ AnandRajaseka và một số câu trả lời tương tự khác, như ở đây , là O (n²), bởi vì sed phải thay thế mỗi khi một dòng mới được thêm vào Không gian mẫu.

Để so sánh,

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

Nếu phiên bản xargs của bạn hỗ trợ cờ -d thì nó sẽ hoạt động

ls  | xargs -d, -L 1 echo

-d là cờ phân cách

Nếu bạn không có -d, thì bạn có thể thử cách sau

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

Các xargs đầu tiên cho phép bạn chỉ định dấu phân cách của bạn là dấu phẩy trong ví dụ này.


3
-dchỉ định dấu phân cách đầu vào với GNU xargs, vì vậy sẽ không hoạt động. Ví dụ thứ hai thể hiện vấn đề tương tự như các giải pháp khác ở đây về dấu phân cách đi lạc ở cuối.
Thor

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

Giải trình:

-e- biểu thị một lệnh được thực thi
:a- là nhãn
/$/N- xác định phạm vi khớp cho dòng mở rộng hiện tại và (N)
s/\n/\\n/;- thay thế tất cả EOL bằng \n
ta;- nhãn goto a nếu khớp thành công

Lấy từ blog của tôi .


2

Bạn có thể dùng:

ls -1 | perl -pe 's/\n$/some_delimiter/'

Điều này không loại trừ dấu phân cách.
Derek Mahar

2

lstạo ra một đầu ra cột khi được kết nối với một đường ống, do đó, -1là dự phòng.

Đây là một câu trả lời perl khác bằng cách sử dụng joinhàm dựng sẵn không để lại dấu phân cách:

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

Việc tối nghĩa -0777làm cho perl đọc tất cả các đầu vào trước khi chạy chương trình.

sed thay thế mà không để lại một dấu phân cách

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

0

lscó tùy chọn -mphân định đầu ra bằng ", "dấu phẩy và dấu cách.

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

đường ống kết quả này trđể loại bỏ khoảng trắng hoặc dấu phẩy sẽ cho phép bạn đặt lại kết quả trđể thay thế dấu phân cách.

trong ví dụ của tôi, tôi thay thế dấu phân cách ,bằng dấu phân cách;

thay thế ;bằng bất cứ ký tự phân cách một ký tự nào bạn thích vì tr chỉ chiếm ký tự đầu tiên trong chuỗi bạn truyền vào dưới dạng đối số.


0

Bạn có thể sử dụng chomp để hợp nhất nhiều dòng trong một dòng:

perl -e 'while (<>) {if (/ \ $ /) {chomp; } in;} 'bad0> kiểm tra

đặt điều kiện ngắt dòng trong if statement. Nó có thể là ký tự đặc biệt hoặc bất kỳ dấu phân cách nào.


0

Phiên bản Perl nhanh với xử lý dấu gạch chéo:

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

Giải thích:

  • perl -E: thực thi Perl với các tính năng hỗ trợ (giả sử, ...)
  • nói: in với trả lại hãng
  • tham gia ",", ARRAY_HERE: tham gia một mảng với ","
  • bản đồ {chomp; $ _} ROWS: xóa khỏi từng dòng mà nhà mạng trả về và trả về kết quả
  • <>: stdin, mỗi dòng là ROW, khớp với bản đồ, nó sẽ tạo ra một mảng của mỗi ROW
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.