Lập chỉ mục một chuỗi trong bash


14

Làm cách nào tôi có thể tham chiếu một chuỗi theo chỉ mục trong sh / bash? Đó là, về cơ bản tách nó.

Tôi đang cố gắng loại bỏ 5 ký tự của một tên tệp. Tất cả các tên có cấu trúc: name_nr_code. Tôi đang cố gắng loại bỏ 5 bit mã chữ số. name_nr_luôn luôn là 10 ký tự.

Có một điều như;

for i in * ; do mv "$i" "$i"[:10] ; done


5
Tại sao bashthẻ nếu bạn đang yêu cầu một shgiải pháp?
Stéphane Chazelas 17/8/2016

Câu trả lời:


14

Đơn giản như thế này.

(bash)

for i in * ; do mv -- "$i" "${i:0:5}" ; done

Voila.

Và một lời giải thích từ Hướng dẫn Bash-Scripting nâng cao ( Chương 10. Thao tác biến ) , (có thêm NOTEdòng nội tuyến để làm nổi bật các lỗi trong hướng dẫn sử dụng đó):

Khai thác chuỗi con

${string:position}

Trích xuất chuỗi con từ $stringtại $position.

Nếu $stringtham số là "*" hoặc "@", thì điều này trích xuất các tham số vị trí, bắt đầu từ $position.

${string:position:length}

Trích xuất các $lengthký tự của chuỗi con từ $stringtại $position.

NOTEthiếu dấu ngoặc kép xung quanh mở rộng tham số! echokhông nên được sử dụng cho dữ liệu tùy ý.

stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                       # abcABC123ABCabc
echo ${stringZ:1}                       # bcABC123ABCabc
echo ${stringZ:7}                       # 23ABCabc 

echo ${stringZ:7:3}                     # 23A
                                        # Three characters of substring.


# Is it possible to index from the right end of the string?

echo ${stringZ:-4}                      # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . . 

echo ${stringZ:(-4)}                    # Cabc
echo ${stringZ: -4}                     # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.

Các đối số vị tríđộ dài có thể được "tham số hóa", nghĩa là, được biểu diễn dưới dạng một biến, thay vì là hằng số.


Nếu $stringtham số là "*" hoặc "@", thì $lengththam số này trích xuất tối đa các tham số vị trí, bắt đầu từ $position.

echo ${*:2}          # Echoes second and following positional parameters.
echo ${@:2}          # Same as above.

echo ${*:2:3}        # Echoes three positional parameters, starting at second.

NOTE: expr substrlà phần mở rộng GNU.

expr substr $string $position $length

Trích xuất các $lengthký tự từ $stringbắt đầu tại $position.

stringZ=abcABC123ABCabc
#       123456789......
#       1-based indexing.

echo `expr substr $stringZ 1 2`           # ab
echo `expr substr $stringZ 4 3`           # ABC

NOTE: Điều đó echolà dư thừa và làm cho nó thậm chí ít đáng tin cậy hơn. Sử dụng expr substr + "$string1" 1 2.

NOTE: exprsẽ trở lại với trạng thái thoát khác không nếu đầu ra là 0 (hoặc -0, 00 ...).


BTW. Cuốn sách có mặt trong kho Ubuntu chính thức như abs-guide.


Nói "vị trí" hơi sai lệch vì nó thực sự là một phần bù, có nghĩa là ${var:1}không trả về giá trị của var"vị trí số 1", mà thực sự là từ vị trí thứ 2.
Kusalananda

Điều đó đúng, nhưng miễn là bạn không đồng ý, có thể có vị trí số 0. Bất cứ khi nào bạn khỏe là đến với tôi.

9

Trong POSIX sh,

  • "${var%?????}"được $vartước của 5 ký tự dấu cuối cùng (hoặc $varnếu $varchứa ít hơn 5 ký tự)

  • "${var%"${var#??????????}"}"là 10 ký tự đầu tiên của $var.

  • "${var%_*}"được $vartước của chuỗi ngắn phù hợp _*vào cuối $var( foo_bar_baz-> foo_bar).
  • "${var%%_*}": cùng một trận đấu dài nhất thay vì trận đấu ngắn nhất ( foo_bar_baz-> foo).
  • nếu bạn muốn lấy foo_bar_: "${var%"${var##*_}"}"( ${var##pattern}giống như ${var%%pattern}nhưng tìm kiếm mẫu ở đầu $varthay vì cuối).

Với zsh:

  • $var[1,-6] cho ký tự đầu tiên đến thứ 6 từ cuối (vì vậy tất cả trừ 5 cuối).
  • $var[1,10] cho 10 ký tự đầu tiên.

Với ksh, bashhoặc zsh:

  • "${var:0:10}": 10 ký tự đầu tiên của $var

Với bashhoặc zsh:

  • "${var:0:-5}": tất cả trừ 5 ký tự cuối cùng (đưa ra lỗi và thoát khỏi tập lệnh nếu $varđược đặt nhưng chứa ít hơn 5 ký tự, cả khi $varkhông được đặt với zsh).

Nếu bạn cần shkhả năng tương thích Bourne , rất khó để thực hiện một cách đáng tin cậy. Nếu bạn có thể đảm bảo kết quả sẽ không kết thúc bằng các ký tự dòng mới, bạn có thể làm:

first_10=`expr " $var" : ' \(.{1,10\}\)'` # beware the exit status
                                          # may be non-zero if the
                                          # result is 0 or 0000000000

all_but_last_5=`expr " $var" : ' \(.*\).\{5\}'`

Bạn cũng sẽ có giới hạn về độ dài $var(khác nhau giữa các hệ thống).

Trong tất cả các giải pháp đó, nếu $varchứa các byte không thể tạo thành một phần của các ký tự hợp lệ, YMMV.


của tôi, họ thực sự đã đưa ra một số cú pháp xấu xí cho bên trong những cái niềng răng đó.
con mèo

2

shkhông cung cấp cách tích hợp để đưa chuỗi con ra khỏi chuỗi (theo như tôi có thể thấy), nhưng với bashbạn có thể làm được

${i:0:10}

Điều này sẽ cung cấp cho bạn mười ký tự đầu tiên của giá trị của biến i.

Các định dạng chung là ${variable:offset:length}.


2

Hầu hết các shell hỗ trợ một số loại mở rộng tham số có thể giúp bạn. Trong bash, bạn có thể sử dụng

substr=${string:4:5} # start at position 4, length 5.

Trong dash, offset không được hỗ trợ, nhưng bạn có thể sử dụng các mẫu hàng đầu và dấu:

remove_first3=${string#???}
remove_last2=${string%??}

0

Trước hết, không sử dụng forvòng lặp cho tên tệp.

Sau đó, một cái gì đó như thế này sẽ giúp.

find ./ -type f | while read filename ;do
  newfilename=$(echo ${filename}|cut -c 1-10)
  mv ${filename} ${newfilename}
done

3
Tại sao nó là xấu để sử dụng forvới tên tập tin?
choroba

Trích dẫn các biến của bạn và sử dụng printfđể an toàn hơn. ... Và read -r.
Kusalananda

3
forVòng lặp của OP vẫn ổn trừ khi có thể bị mất --. Tôi có thể thấy ít nhất 10 lỗi trong 4 dòng mã của bạn! nhiều trong số đó thực hành xấu được biết đến như giả sử tên tệp là một dòng, sử dụng tiếng vang, dấu ngoặc kép bị thiếu
Stéphane Chazelas
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.