bash: cách ngắn nhất để có cột đầu ra thứ n


165

Giả sử trong ngày làm việc của bạn, bạn liên tục gặp phải dạng đầu ra được chia cột sau đây từ một số lệnh trong bash (trong trường hợp của tôi là thực thi svn sttrong thư mục làm việc Rails của tôi):

?       changes.patch
M       app/models/superman.rb
A       app/models/superwoman.rb

để làm việc với đầu ra của lệnh của bạn - trong trường hợp này là tên tệp - một số loại phân tích được yêu cầu để cột thứ hai có thể được sử dụng làm đầu vào cho lệnh tiếp theo.

Những gì tôi đã làm là sử dụng awkđể lấy ở cột thứ hai, ví dụ như khi tôi muốn xóa tất cả các tệp (không phải đó là một usecase điển hình :), tôi sẽ làm:

svn st | awk '{print $2}' | xargs rm

Vì tôi gõ cái này rất nhiều, một câu hỏi tự nhiên là: có cách nào ngắn hơn (vì thế mát hơn) để hoàn thành việc này trong bash không?

LƯU Ý: Những gì tôi đang hỏi về cơ bản là một câu hỏi lệnh shell mặc dù ví dụ cụ thể của tôi nằm trong quy trình làm việc svn của tôi. Nếu bạn cảm thấy quy trình làm việc đó thật ngớ ngẩn và đề xuất một cách tiếp cận khác, tôi có thể sẽ không bỏ phiếu cho bạn, nhưng những người khác có thể, vì câu hỏi ở đây thực sự là làm thế nào để có được đầu ra lệnh cột thứ n trong bash, theo cách ngắn nhất có thể . Cảm ơn :)


1
Khi bạn sử dụng một lệnh thường xuyên, bạn nên tạo một tập lệnh và đặt nó vào đường dẫn của bạn. Bạn chỉ có thể tạo một hàm trong bashrc nếu bạn thích. Tôi không thấy điểm giảm biểu thức chọn cột.
Lynch

1
Bạn đúng, và tôi có thể làm điều đó. 'Điểm' là nhiệm vụ cho những cách mới để làm công việc trong bash, cho mục đích học tập nhưng chủ yếu là để giải trí :)
Sv1

1
Ngoài ra, bạn không có .bashrc khi ssh-ing ở đâu đó, vì vậy thật hữu ích khi biết cách của bạn xung quanh mà không có nó.
Kos

Câu trả lời:


125

Bạn có thể sử dụng cutđể truy cập vào trường thứ hai:

cut -f2

Chỉnh sửa: Xin lỗi, đã không nhận ra rằng SVN không sử dụng các tab trong đầu ra của nó, vì vậy điều đó hơi vô dụng. Bạn có thể điều chỉnh cutđầu ra nhưng nó hơi mong manh - một cái gì đó giống như cut -c 10- sẽ hoạt động, nhưng giá trị chính xác sẽ phụ thuộc vào thiết lập của bạn.

Một lựa chọn khác là: sed 's/.\s\+//'


1
Cố gắng sử dụng cut -f2với svn stđầu ra. Bạn sẽ thấy nó không hoạt động.
Lynch

Tôi giả định rằng thực tế svn stsẽ sử dụng các tab trong đầu ra của nó. Sau một số thử nghiệm có vẻ như đây không phải là trường hợp. Chỉ trích.
porges

21
Vượt qua một dấu phân cách thích hợp để -dcho phép điều này hoạt động.
Yogh

6
Để mở rộng những gì @Yogh đã nói, đối với các không gian là dấu phân cách, nó sẽ trông như thế cut -d" " -f2.
adamyonk

W / o awk trên thiết bị xuất chuẩn sử dụng xargs: svn st | xargs | cắt -d ""
-f2

106

Để thực hiện điều tương tự như:

svn st | awk '{print $2}' | xargs rm

chỉ sử dụng bash bạn có thể sử dụng:

svn st | while read a b; do rm "$b"; done

Cấp, nó không ngắn hơn, nhưng nó hiệu quả hơn một chút và nó xử lý khoảng trắng trong tên tệp của bạn một cách chính xác.


Là gì ab, làm thế nào để có được chúng?
Timo

1
@Timo ađại diện cho cột đầu tiên và bđại diện cho các cột còn lại. Nếu bạn muốn in cột thứ hai, sử dụng read a b c;và sau đó sử dụng echothay vì rm. Tôi đã sử dụng điều này để có được một loạt các quá trình ID theo mô hình grep, vì vậy tôi có thể làm gián đoạn tất cả chúng.
Matt Kleinsmith

36

Tôi thấy mình trong tình huống tương tự và cuối cùng đã thêm các bí danh này vào .profiletệp của mình :

alias c1="awk '{print \$1}'"
alias c2="awk '{print \$2}'"
alias c3="awk '{print \$3}'"
alias c4="awk '{print \$4}'"
alias c5="awk '{print \$5}'"
alias c6="awk '{print \$6}'"
alias c7="awk '{print \$7}'"
alias c8="awk '{print \$8}'"
alias c9="awk '{print \$9}'"

Điều này cho phép tôi viết những thứ như thế này:

svn st | c2 | xargs rm

15
Hàm Bash thường hữu ích hơn. Tôi đã làm điều này: function c() { awk "{print \$$1}" } Sau đó, bạn có thể làm: svn st | c 2 | xargs rm
Aissen

8
Nhưng sau đó tôi cần gõ thêm một khoảng trống. Điều đó quá mệt mỏi :)
StackedCrooking

16

Hãy thử zsh. Nó hỗ trợ bí danh hậu tố, vì vậy bạn có thể định nghĩa X trong .zshrc của mình thành

alias -g X="| cut -d' ' -f2"

sau đó bạn có thể làm:

cat file X

Bạn có thể tiến thêm một bước và xác định nó cho cột thứ n:

alias -g X2="| cut -d' ' -f2"
alias -g X1="| cut -d' ' -f1"
alias -g X3="| cut -d' ' -f3"

Nó sẽ xuất cột thứ n của tập tin "tập tin". Bạn cũng có thể làm điều này cho đầu ra grep hoặc đầu ra ít hơn. Điều này rất tiện dụng và là một tính năng giết người của zsh.

Bạn có thể tiến thêm một bước và xác định D là:

alias -g D="|xargs rm"

Bây giờ bạn có thể gõ:

cat file X1 D

để xóa tất cả các tệp được đề cập trong cột đầu tiên của tệp "tệp".

Nếu bạn biết bash, zsh không có nhiều thay đổi ngoại trừ một số tính năng mới.

HTH Chris


à, tôi thấy bạn đã cập nhật câu trả lời của bạn trong khi tôi đang gõ bình luận của mình ở trên :) Bạn có thể tự động xác định cột nào sẽ nhận được không, hoặc tôi có cần n-lines trong .zshrc của tôi không (điều đó không quan trọng, chỉ là tò mò)
Sv1

Tôi đã chỉnh sửa bài viết của mình hơn nữa và xác định hậu tố "D" để xóa các tập tin. Theo tôi biết bạn sẽ phải thêm một dòng cho mỗi hậu tố.
Chris

Hoặc làm cho nó trở thành tham số đầu tiên của lệnh X. Không ai trong số này yêu cầu zsh hoặc bí danh, ngoại trừ ý tưởng kỳ dị là đặt một đường ống trong bí danh.
tripleee

1
Tôi không hiểu tại sao tất cả các bạn dường như thích cuttùy chọn này. Nó chỉ đơn giản là không hoạt động trên máy của tôi bằng cách sử dụng đầu ra của svn st. Hãy thử svn st | cut -d' ' -f2chỉ để xem những gì xảy ra.
Lynch

@ Sv1 bạn có thể viết một vòng lặp để xác định các bí danh, vì bí danh chỉ là một lệnh.
driftcatcher

8

Bởi vì bạn dường như không quen thuộc với các kịch bản, đây là một ví dụ.

#!/bin/sh
# usage: svn st | x 2 | xargs rm
col=$1
shift
awk -v col="$col" '{print $col}' "${@--}"

Nếu bạn lưu cái này vào ~/bin/xvà chắc chắn ~/binlà trong PATH(bây giờ đó là thứ bạn có thể và nên đặt vào .bashrc), bạn có lệnh ngắn nhất có thể để trích xuất cột n; x n.

Tập lệnh nên thực hiện kiểm tra lỗi và bảo lãnh thích hợp nếu được gọi với một đối số không phải là số hoặc số lượng đối số không chính xác, v.v; nhưng mở rộng trên phiên bản thiết yếu này sẽ ở đơn vị 102.

Có thể bạn sẽ muốn mở rộng tập lệnh để cho phép một dấu phân cách cột khác. Awk theo mặc định phân tích cú pháp đầu vào vào các trường trên khoảng trắng; sử dụng một delimiter khác nhau, sử dụng -F ':'nơi :là dấu phân cách mới. Việc thực hiện điều này như là một tùy chọn cho kịch bản làm cho nó dài hơn một chút, vì vậy tôi sẽ để nó như một bài tập cho người đọc.


Sử dụng

Đưa ra một tập tin file:

1 2 3
4 5 6

Bạn có thể chuyển nó qua stdin (sử dụng một thứ vô dụngcat chỉ để giữ chỗ cho thứ gì đó hữu ích hơn);

$ cat file | sh script.sh 2
2
5

Hoặc cung cấp nó như là một đối số cho kịch bản:

$ sh script.sh 2 file
2
5

Ở đây, sh script.shgiả sử rằng tập lệnh được lưu như script.shtrong thư mục hiện tại; nếu bạn lưu nó với một tên hữu ích hơn ở đâu đó PATHvà đánh dấu nó có thể thực thi được, như trong hướng dẫn ở trên, rõ ràng sử dụng tên hữu ích thay thế (và không sh).


Ý tưởng hay, nhưng không hoàn toàn phù hợp với tôi như trên sh hay bash. Công việc này: #! / Bin / bash col = $ 1 ca awk "{print \ $$ col}"
mgk

Cảm ơn vì nhận xét muộn này. Cập nhật với sửa chữa của bạn.
tripleee

1
tốt hơnawk -v col=$col ...
fedorqui 'SO ngừng làm hại'

@fedorqui Cảm ơn phản hồi của bạn, ở đây và ở nơi khác! Cập nhật câu trả lời. Bây giờ nó dài hơn một chút vì vậy nếu bạn muốn tập lệnh hoàn toàn ngắn nhất bạn có thể có, hãy xem lịch sử sửa đổi cho phiên bản gốc.
tripleee

1
@fedorqui Cảm ơn rất nhiều! Đã thêm một cảnh báo nhỏ vào catví dụ vì lý do kích động (-:
tripleee

5

Có vẻ như bạn đã có một giải pháp. Để làm cho mọi việc dễ dàng hơn, tại sao bạn không đặt lệnh của bạn trong tập lệnh bash (với một tên ngắn) và chỉ chạy lệnh đó thay vì gõ lệnh 'dài' đó mỗi lần?


Theo quan điểm của tôi, nó không phải là "tuyệt". Tại sao? Chà, tôi không muốn làm ngập .bashrc của mình bằng nhiều phím tắt khác nhau để làm điều này và về cơ bản là viết một meta-shell. Nó khá bí danh như nó là. Phải nói rằng, đề nghị của bạn không phải là một điều xấu.
Sv1

2
.bashrc? Tại sao bạn lại làm lộn xộn nó với những thứ không liên quan đến bash. Những gì tôi làm là tôi viết các tập lệnh riêng lẻ cho mỗi 'bộ' lệnh và đặt tất cả chúng vào một ~/scriptsthư mục. Sau đó thêm toàn bộ ~/scriptsvào của tôi PATHđể tôi chỉ có thể gọi họ bằng tên khi tôi cần.
Tarek Fadel

4
Sau đó, không đặt nó trong .bashrc của bạn. Tạo một tập lệnh trong ~ / bin và đảm bảo rằng nó nằm trên PATH của bạn.
tripleee

2

Nếu bạn ổn với việc chọn cột theo cách thủ công, bạn có thể rất nhanh bằng cách sử dụng chọn :

svn st | pick | xargs rm

Chỉ cần đi đến bất kỳ ô nào của cột thứ 2, nhấn cvà sau đó nhấnenter


Tôi đã thử công cụ "chọn" mà bạn đã tham khảo và tôi rất thích nó. Rất tốt cho nhiều tình huống tôi muốn in các cột đã chọn nhưng không muốn gõ "awk '{print $ 3}'" chỉ để có được cột mong muốn. Thật không may, tên xung đột với một công cụ "chọn" khác có vẻ phổ biến hơn - nó có thể được cài đặt với "apt install pick". Vì vậy, tôi đã đổi tên công cụ của bạn thành "pickk" trên hệ thống của mình và có ý định tiếp tục sử dụng nó. Cảm ơn đã đăng một tài liệu tham khảo.
William Dye

1

Lưu ý, đường dẫn tệp đó không phải ở cột thứ hai của đầu ra svn st. Ví dụ: nếu bạn sửa đổi tệp và sửa đổi thuộc tính của nó, nó sẽ là cột thứ 3.

Xem các ví dụ đầu ra có thể có trong:

svn help st

Ví dụ đầu ra:

 M     wc/bar.c
A  +   wc/qax.c

Tôi đề nghị cắt 8 ký tự đầu tiên bằng cách:

svn st | cut -c8- | while read FILE; do echo whatever with "$FILE"; done

Nếu bạn muốn chắc chắn 100% và xử lý các tên tệp ưa thích với khoảng trắng ở cuối, chẳng hạn, bạn cần phân tích đầu ra xml:

svn st --xml | grep -o 'path=".*"' | sed 's/^path="//; s/"$//'

Tất nhiên bạn có thể muốn sử dụng một số trình phân tích cú pháp XML thực sự thay vì grep / sed.

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.