2009-03-02 52 views
73

Làm cách nào để Bash đổi tên một loạt các gói để xóa số phiên bản của chúng? Tôi đã chơi đùa với cả hai expr%%, không có kết quả.Đổi tên tệp hàng loạt bằng Bash

Ví dụ:

Xft2-2.1.13.pkg trở thành Xft2.pkg

jasper-1.900.1.pkg trở thành jasper.pkg

xorg-libXrandr-1.2.3.pkg trở nên sử dụng xorg-libXrandr.pkg

+1

Tôi định sử dụng thường xuyên, như một tập lệnh dùng một lần. Bất kỳ hệ thống nào tôi sẽ sử dụng đều sẽ bị bash, vì vậy tôi không sợ các bashism khá tiện dụng. –

+0

Thông thường, xem thêm https://stackoverflow.com/questions/28725333/looping-over-pairs-of-values-in-bash – tripleee

Trả lời

138

Bạn có thể sử dụng tính năng mở rộng thông số bash của

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done 

Quotes là cần thiết cho tên tập tin với không gian.

+1

Tôi thích điều này tốt nhất cho đến nay! –

+1

Tôi thích nó quá, nhưng nó sử dụng các tính năng bash-cụ thể ... Tôi thường không sử dụng chúng trong lợi của sh "tiêu chuẩn". –

+2

Đối với công cụ tương tác một lần, tôi luôn sử dụng nó thay vì sed-fu. Nếu nó là một kịch bản, vâng, tránh bashisms. – richq

3

tốt hơn sed cho điều này, một cái gì đó như:

find . -type f -name "*.pkg" | 
sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' | 
while read nameA nameB; do 
    mv $nameA $nameB; 
done 

tìm lên các biểu thức chính quy là trái như một bài tập (như là đối phó với tên tập tin bao gồm cả khoảng trắng)

+0

Cảm ơn: Không gian không được sử dụng trong tên. –

1

Điều này dường như làm việc giả định rằng

  • tất cả mọi thứ kết thúc với $ pkg
  • phiên bản của bạn # 's luôn bắt đầu với một "-"

dải khỏi pkg, sau đó lột - ..

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done 
+0

Có một số gói có dấu nối trong tên của chúng (xem ví dụ thứ ba). Điều này có ảnh hưởng đến mã của bạn không? –

+1

Bạn có thể chạy nhiều biểu thức sed bằng cách sử dụng '-e'. Ví dụ. 'sed -e 's/\. pkg // g' -e 's /-.*// g''. – nojak

+0

[Không phân tích cú pháp 'ls' output] (https://mywiki.wooledge.org/ParsingLs) và [báo giá biến của bạn.] (/ Q/10067266) Xem thêm http://shellcheck.net/ để chẩn đoán chung vấn đề và antipatterns trong shell script. – tripleee

7

tôi sẽ làm một cái gì đó như thế này:

for file in *.pkg ; do 
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg 
done 

phải tất cả các tập tin của bạn nằm trong thư mục hiện hành. Nếu không, cố gắng sử dụng tìm kiếm như được tư vấn ở trên bởi Javier.

EDIT: Ngoài ra, phiên bản này không sử dụng bất kỳ tính năng bash cụ thể nào, như các tính năng khác ở trên, dẫn bạn đến tính di động nhiều hơn.

+0

Tất cả đều nằm trong cùng một thư mục. Bạn nghĩ gì về câu trả lời của rq? –

+0

Tôi không thích sử dụng các tính năng bash-cụ thể, nhưng thay vào đó là tiêu chuẩn sh hơn. –

+0

Tôi cho rằng đây là một cách nhanh chóng đổi tên, không phải để viết nền tảng thế hệ tiếp theo, Ứng dụng doanh nghiệp di động ;-) – richq

23

Nếu tất cả các file nằm trong thư mục cùng một chuỗi

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh 

sẽ làm công việc của bạn. Lệnh sed sẽ tạo một chuỗi các lệnh mv, sau đó bạn có thể đưa vào trình bao. Cách tốt nhất để chạy đường ống đầu tiên mà không có dấu vết | sh để xác minh rằng lệnh đó thực hiện những gì bạn muốn.

Để recurse thông qua nhiều thư mục sử dụng giống như

find . -type f | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh 

Lưu ý rằng trong sed biểu thức chính quy nhóm chuỗi là dấu ngoặc trước bởi một dấu gạch chéo, \(\), chứ không phải là dấu ngoặc đơn ().

+0

Hãy tha thứ cho sự ngây thơ của tôi, nhưng sẽ không thoát khỏi dấu ngoặc đơn với dấu gạch chéo ngược làm cho chúng được coi là ký tự chữ? – devios1

+2

Trong sed chuỗi thứ tự biểu thức chính quy là \ (, chứ không phải là một dấu ngoặc đơn. –

+0

không hoạt động đối với tôi, sẽ rất vui khi nhận được sự trợ giúp của bạn .... Hậu tố của tệp TỪ được nối vào tệp TO: Tìm kiếm $ \. \ type \ |/\ 2effectedkeyboard \ 3 "/ p '| sh >>>>>>> OUTPUT >>>> mv: đổi tên ./base/src/main/java/com/anysoftkeyboard/base/dictionaries thành ./base/src/ main/java/com/effectedkeyboard/base/từ điển/từ điển: Không có tập tin hoặc thư mục như vậy –

0

Cảm ơn bạn đã trả lời câu hỏi này. Tôi cũng có một số vấn đề. Di chuyển tệp .nzb.queued sang tệp .nzb. Nó có không gian và các hành trình khác trong tên tệp và điều này giải quyết được vấn đề của tôi:

find . -type f -name "*.nzb.queued" | 
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" | 
sh 

Nó dựa trên câu trả lời của Diomidis Spinellis.

Regex tạo một nhóm cho toàn bộ tên tệp và một nhóm cho phần trước .nzb.queued và sau đó tạo lệnh di chuyển shell. Với các chuỗi được trích dẫn. Điều này cũng tránh tạo ra một vòng lặp trong kịch bản shell vì điều này đã được thực hiện bởi sed.

+0

Cần lưu ý rằng điều này chỉ đúng với GNU sed. tùy chọn '-n' theo cách tương tự. – ocodo

1

Tôi có nhiều tệp *.txt để đổi tên thành .sql trong cùng một thư mục. dưới đây làm việc cho tôi:

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done 
+2

Bạn có thể cải thiện câu trả lời của mình bằng cách tóm tắt câu trả lời cho trường hợp sử dụng thực tế của OP. – dakab

+0

Điều này có nhiều vấn đề cũng như một cú pháp rõ ràng x lỗi. Xem http://shellcheck.net/ để bắt đầu. – tripleee

1

Đây là một tương đương gần POSIX của currently accepted answer. Thao tác này chỉ mở rộng tham số ${variable/substring/replacement} Bash chỉ dành cho một phiên bản có sẵn trong bất kỳ trình bao tương thích Bourne nào.

for i in ./*.pkg; do 
    mv "$i" "${i%-[0-9.]*.pkg}.pkg" 
done 

Các tham số mở rộng ${variable%pattern} sản xuất giá trị của variable với bất kỳ hậu tố mà phù hợp pattern gỡ bỏ. (Ngoài ra còn có ${variable#pattern} để xóa tiền tố.)

Tôi đã giữ subpattern -[0-9.]* từ câu trả lời được chấp nhận mặc dù nó có thể gây hiểu nhầm. Nó không phải là một biểu thức chính quy, mà là một mẫu hình cầu; do đó, nó không có nghĩa là "một dấu gạch ngang theo sau là số không hoặc nhiều số hoặc dấu chấm". Thay vào đó, nó có nghĩa là "một dấu gạch ngang, theo sau là một số hoặc một dấu chấm, tiếp theo là bất cứ điều gì". "Mọi thứ" sẽ là trận đấu ngắn nhất có thể, không phải là dài nhất. (Bash cung cấp ##%% cho cắt tỉa tiền tố có thể dài hoặc hậu tố, chứ không phải là ngắn nhất.)

0

Chúng ta có thể giả định sed có sẵn trên bất kỳ * nix, nhưng chúng tôi không thể chắc chắn nó sẽ hỗ trợ sed -n để tạo lệnh mv. (LƯU Ý: Chỉ GNU sed thực hiện điều này.)

Mặc dù vậy, bash xây dựng và sed, chúng ta có thể nhanh chóng roi lên một hàm vỏ để làm điều này.

sedrename() { 
    if [ $# -gt 1 ]; then 
    sed_pattern=$1 
    shift 
    for file in $(ls [email protected]); do 
     mv -v "$file" "$(sed $sed_pattern <<< $file)" 
    done 
    else 
    echo "usage: $0 sed_pattern files..." 
    fi 
} 

Cách sử dụng

sedrename 's|\(.*\)\(-[0-9.]*\.pkg\)|\1\2|' *.pkg 

before: 

./Xft2-2.1.13.pkg 
./jasper-1.900.1.pkg 
./xorg-libXrandr-1.2.3.pkg 

after: 

./Xft2.pkg 
./jasper.pkg 
./xorg-libXrandr.pkg 

Tạo thư mục mục tiêu:

Kể từ mv không tự động tạo thư mục mục tiêu chúng ta không thể sử dụng phiên bản ban đầu của chúng ta về sedrename.

Đó là một sự thay đổi khá nhỏ, vì vậy nó sẽ được tốt đẹp để bao gồm tính năng:

Chúng tôi sẽ cần một chức năng tiện ích, abspath (hoặc đường dẫn tuyệt đối) từ bash không có build này.

abspath() { case "$1" in 
       /*)printf "%s\n" "$1";; 
       *)printf "%s\n" "$PWD/$1";; 
      esac; } 

một khi chúng ta có mà chúng ta có thể tạo ra các thư mục đích (s) cho một sed/đổi tên mẫu trong đó bao gồm cấu trúc thư mục mới.

Điều này sẽ đảm bảo chúng tôi biết tên của các thư mục đích của chúng tôi. Khi chúng tôi đổi tên, chúng tôi sẽ cần sử dụng nó trên tên tệp đích.

# generate the rename target 
target="$(sed $sed_pattern <<< $file)" 

# Use absolute path of the rename target to make target folder structure 
mkdir -p "$(dirname $(abspath $target))" 

# finally move the file to the target name/folders 
mv -v "$file" "$target" 

Dưới đây là thư mục kịch bản đầy đủ ý thức ...

sedrename() { 
    if [ $# -gt 1 ]; then 
    sed_pattern=$1 
    shift 
    for file in $(ls [email protected]); do 
     target="$(sed $sed_pattern <<< $file)" 
     mkdir -p "$(dirname $(abspath $target))" 
     mv -v "$file" "$target" 
    done 
    else 
    echo "usage: $0 sed_pattern files..." 
    fi 
} 

Tất nhiên, nó vẫn hoạt động khi chúng ta không có mục tiêu cụ thể thư mục quá.

Nếu chúng ta muốn đặt tất cả các bài hát vào một thư mục, ./Beethoven/ chúng ta có thể làm điều này:

Cách sử dụng

sedrename 's|Beethoven - |Beethoven/|g' *.mp3 

before: 

./Beethoven - Fur Elise.mp3 
./Beethoven - Moonlight Sonata.mp3 
./Beethoven - Ode to Joy.mp3 
./Beethoven - Rage Over the Lost Penny.mp3 

after: 

./Beethoven/Fur Elise.mp3 
./Beethoven/Moonlight Sonata.mp3 
./Beethoven/Ode to Joy.mp3 
./Beethoven/Rage Over the Lost Penny.mp3 

Bonus tròn ...

Sử dụng kịch bản này để di chuyển file từ các thư mục vào một thư mục:

Giả sử chúng tôi muốn tập hợp tất cả các tệp phù hợp và đặt chúng trong f hiện tại già đi, chúng ta có thể làm điều đó:

sedrename 's|.*/||' **/*.mp3 

before: 

./Beethoven/Fur Elise.mp3 
./Beethoven/Moonlight Sonata.mp3 
./Beethoven/Ode to Joy.mp3 
./Beethoven/Rage Over the Lost Penny.mp3 

after: 

./Beethoven/ # (now empty) 
./Fur Elise.mp3 
./Moonlight Sonata.mp3 
./Ode to Joy.mp3 
./Rage Over the Lost Penny.mp3 

Lưu ý về mẫu regex sed

Regular quy tắc mẫu sed áp dụng trong kịch bản này, các mẫu không PCRE (Regular Expressions Perl Tương thích). Bạn có thể có sed cú pháp biểu thức chính quy mở rộng, sử dụng sed -r hoặc sed -E tùy thuộc vào nền tảng của bạn.

Xem POSIX tuân thủ man re_format để có mô tả đầy đủ về sed mẫu regexp cơ bản và mở rộng.

Các vấn đề liên quan