怎樣寫 bash 腳本批量重命名文件?

將一個文件夾里所有" glyphicons_數字_字元串.png "這樣的文件重命名為" 字元串.png ",即去掉黑色斜體部分的文件名內容,用shell命令或腳本怎麼實現?


Debian/Ubuntu 自帶一個叫做 rename 的 Perl 腳本,用法如下:

$ rename "s/^glyphicons_[0-9]+_//" glyphicons_*.png

Mac 用戶可以用這個改進版:

$ brew install rename

P.S. 傳說這個 rename 是當初 Larry Wall 為了展示 Perl 的強大所寫,出現在第一版的《Programming Perl》書中,僅用了 8 行代碼。


#!/bin/bash

#path變數指向你的目標目錄,找好目標目錄並取消下方行的注釋

#path=/your/dir

for i in `ls $path | grep "^glyphicons_[0-9]*_.*.png$"`

do

name=`echo $i | sed "s/^glyphicons_[0-9]*_//g"`

mv $i $name

done

#如有錯誤,請指出,謝謝


$ ls -1 glyphicons_[0-9]*.png|sed "s/glyphicons_([0-9][0-9]*)_(.*).png/mv 2.png/"|sh -v

正則表達式是一切字元串處理的基礎,掌握它,一切字元串問題迎刃而解。


自己搞定了~_~! 雖然很搓,但希望能拋磚引玉。。。

#!/bin/sh

# generate files
# for debug
#`ls | grep glyphicons &> input`

# for debug
#i=`cat "input"`
i=`ls | grep glyphicons`

set -x

# for debug
# note: echo "" &> temp is not an empty file
# use `od -c temp` to check out
`cat /dev/null &> temp`

for j in $i
do
a=${j//_/ }
#echo $a
c=0
s=""
so=""
for k in $a
do
if [ $c == 0 ]; then
so=$k
elif [ $c == 1 ]; then
so=$so"_"$k
elif [ $c == 2 ]; then
so=$so"_"$k
s=$s$k
else
so=$so"_"$k
s=$s"_"$k
fi
c=`expr $c + 1`
done
# for debug
echo "mv -i $so $s" &>&> temp
`mv -i $so $s`
done


zsh 湊個熱鬧:

zmv "glyphicons_*_(*).png" "$1.png"


如果最後的字元串沒有重複的話

for i in `ls|grep glyphicons`
do
newName=`echo $i |cut -d_ -f3`
mv $i $newName
done


純粹使用bash內置命令的話,一行命令就夠了:

==================建立測試文件====================

$ touch glyphicons_315911_linux{a..g}.png
$ ls *.png
glyphicons_315911_linuxa.png glyphicons_315911_linuxe.png
glyphicons_315911_linuxb.png glyphicons_315911_linuxf.png
glyphicons_315911_linuxc.png glyphicons_315911_linuxg.png
glyphicons_315911_linuxd.png

==================運行腳本========================

$ for i in `ls glyphicons_*.png`; do mv $i ${i##glyphicons_*_}; done
$ ls *.png
linuxa.png linuxc.png linuxe.png linuxg.png
linuxb.png linuxd.png linuxf.png
#最後刪除測試文件,此命令請謹慎使用
$ rm -f *.png


隨手寫的,把當前目錄下文件名中含有的某個單詞換成另一個,你把目標單詞換成空就是了。沒有做找不到文件的處理,自己可以加一下。

#!/bin/sh

# This script is used to change the input current_word in names of files to appointed word.

if [ $# -ne 2 ];then

echo Usage: sh $0 current_word target_word

exit

fi

current_word=$1

target_word=$2

for file_name in $(ls)

do

target_name=$(echo ${file_name} | sed "s/${current_word}/${target_word}/g")

mv ${file_name} ${target_name}

done


# ls
glyphicons_123456_a.png glyphicons_123456_b.png glyphicons_123456_c.png glyphicons_123456_d.png glyphicons_123456_e.png
# ls |awk -F"_" "{print "mv " $1"_"$2"_"$3 " " $3}"|bash

===

改良了一下。

ls|awk -F"_" "{print "mv " $0 " " $3}"|bash

+++++20170627再改。剛剛看見有夥伴點贊,然後看了下評論上面的答案我也不知道當時怎麼想的。。加了個 。。。直接下面這樣就可以了。把 直接換成空格。

ls|awk -F"_" "{print "mv " $0 " " $3}"|bash


rename 『glyphicons_數字_』 『』 *


有個rename命令,跟sed的s用法一樣


前面的好多解答追求短小快的太多,其中不乏技巧,但是從一個程序員的角度看,沒有注意一些細節。

1. 規則不夠準確, 比如glyphicons_*.png的情況,如果遇到glyphicons_1a_A.png的文件,也就是在兩個"_"中間部分不完全是數字的情況,按照樓主的要求,它不滿足條件,但該規則會認為滿足. 同樣道理上面的sed也是同樣問題。

如果使用

sed -E "s/glyphicons_[0-9]*_//"

應該會好些,「*」說明中間的數字可以出現多次,但不能有非數字元號。

當然這一點也有題主說明不太清楚的地方

2. 實際情況考慮不周,比如有兩個文件名:
glyphicons_0_same.png
glyphicons_1_same.png

它們可以在同一個目錄下共存,因為文件名不同,當去除前面的的部分後,剩下的部分都是same.png,也就是同名文件,如果使用mv命令,因為是移動到同一個目錄下,那麼先被移動的就會被後面的覆蓋。

沒有用過rename和zsh,所以不知道它們是如何處理同名文件的。

所以,看起來需要更多的語句處理衝突。我這裡不寫了,因為寫出來的肯定沒有他人寫得漂亮。

之所以說上面這些,不是說前面的答案不好,其實它們有體現了shell命令的簡介的風格,而且用多種方式達成一個目的,看上去賞心悅目。但在實際處理中需要考慮多種的因素,否則出現數據丟失或者莫名其妙的結果。


推薦閱讀:

mysql中的一個數據表,共有14萬行左右,其中一列是公式,怎麼把每一個公式導出成一個txt文件?
幹了四年運維,但是都是初級的,現在想學 Linux 運維,不知道馬哥和老男孩哪個做得更好一些?
Ubuntu server 14.04 需要經常 update 嗎?
運維工程師和架構師區別?
為什麼很多公司都自主開發監控系統?(Linux運維方面)

TAG:Linux | 腳本 | Bash | Linux運維 |