ลบช่องว่างเครื่องหมายขีดกลางและขีดล่างในชื่อไฟล์หรือไม่


10

คำสั่งที่ดีในการลบช่องว่างยัติภังค์และขีดล่างจากไฟล์ทั้งหมดในไดเรกทอรีหรือไฟล์ที่เลือกคืออะไร

ฉันใช้คำสั่งต่อไปนี้กับ Thunar Custom Actions เพื่อ slugify ชื่อไฟล์:

for file in %N; do mv "$file" "$(echo "$file" | tr -s ' ' | tr ' A-Z' '-a-z' | tr -s '-' | tr -c '[:alnum:][:cntrl:].' '-')"; done

แต่คำสั่งนั้นจะแทนที่ช่องว่างด้วยเครื่องหมายขีดคั่น / ขีดกลางและอักขระตัวพิมพ์เล็กที่ต่อท้าย

ฉันใช้คำสั่งต่อไปนี้ในเทอร์มินัลเพื่อลบช่องว่างออกจากชื่อไฟล์หลายพันรายการในโฟลเดอร์และทำงานได้อย่างรวดเร็ว:

 rename "s/ //g" *

อีกครั้งจะลบช่องว่างเท่านั้นไม่ใช่เครื่องหมายขีดคั่น / ขีดกลางและขีดล่างเช่นกัน

เป็นการดีที่ฉันไม่ต้องการช่องว่างขีดกลาง / ขีดกลางและขีดล่างในชื่อไฟล์ของฉัน และจะเป็นการดีถ้าคำสั่งสามารถใช้กับ Thunar Custom Actions ในไฟล์ที่เลือก


2
ฉันสังเกตเห็นปัญหาข้อหนึ่งที่มีวิธีแก้ไขปัญหาที่เสนอจำนวนมากไม่ได้ตรวจสอบการมีอยู่ของชื่อ "ใหม่" อย่างถูกต้องก่อนที่จะแก้ไขไฟล์ การไม่ทำเช่นนั้นอาจเป็นสาเหตุของปัญหาได้มากมาย
mdpc

เป็นไปได้หรือไม่ที่จะแก้ไขคำสั่งของ John1024 เพื่อตรวจสอบสิ่งนั้น?
user8547

@ user8547rename -i "s/[-_ ]//g" *
Sparhawk

ขอบคุณ Sparhawk อนึ่งสำหรับผู้ที่สนใจใช้สิ่งนี้เป็น Thunar Custom Action คำสั่งสำหรับ Thunar คือ: สำหรับไฟล์ใน% N; ทำ mv "$ file" echo $file | sed -e 's/[ _-]//g'; เสร็จแล้ว
user8547

คำตอบ:


11

รุ่นrenameที่มาพร้อมกับperlแพ็คเกจสนับสนุนนิพจน์ทั่วไป:

rename "s/[-_ ]//g" *

อีกวิธีหนึ่งคือ

rename -i "s/[-_ ]//g" *

การ-iตั้งค่าสถานะจะทำให้การrenameใช้โหมดการโต้ตอบแจ้งให้ถ้าเป้าหมายมีอยู่แล้วแทนที่จะเขียนทับเงียบ

การเปลี่ยนชื่อของ Perl prenameบางครั้งเรียกว่า

เปลี่ยนชื่อของ Perl เมื่อเทียบกับการเปลี่ยนชื่อของ Ut-linux

บนระบบที่คล้ายกับ Debian การเปลี่ยนชื่อของ perl นั้นน่าจะเป็นค่าเริ่มต้นและคำสั่งข้างต้นควรจะใช้งานได้

ในการแจกแจงบางอย่างrenameยูทิลิตี้จาก util-linux เป็นค่าเริ่มต้น ยูทิลิตี้นี้จะสมบูรณ์ไม่เข้ากันกับของ renamePerl

  • ทั้งหมด:อันดับแรกตรวจสอบเพื่อดูว่า Perl renameมีให้บริการภายใต้ชื่อprenameหรือไม่

  • Debian:การเปลี่ยนชื่อของ Perl ควรเป็นค่าเริ่มต้น prenameนอกจากนี้ยังสามารถใช้เป็น renameถึงแม้ว่าการปฏิบัติการจะอยู่ภายใต้การควบคุมของ/etc/alternativesและอาจมีการเปลี่ยนแปลงกับสิ่งที่แตกต่าง

  • ArchLinux: Run และคำสั่งที่สามารถใช้ได้เป็นpacman -S perl-rename perl-renameสำหรับชื่อที่สะดวกกว่าให้สร้างนามแฝง (หมวกปลาย: ChiseledAbs)

  • Mac OSXตามคำตอบนี้ , renameสามารถติดตั้งบน OSX ใช้ Homebrew ผ่าน:

    brew install rename 
  • ดาวน์โหลดโดยตรง: renameนอกจากนี้ยังมีจากพระ Perl:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename

ฉันคิดว่าขึ้นอยู่กับสิ่งที่renameคุณพูดถึง รายการจากutil-linux -2.24.2-1.fc20.x86_64 ไม่สนับสนุนนิพจน์ทั่วไป
Cristian Ciupitu

1
@CristianCiupitu ฉันเพิ่งตรวจสอบหน้าคนสำหรับรุ่นของการเปลี่ยนชื่อที่คุณพบ ตามอาร์กิวเมนต์เวอร์ชันของrenameOP ที่ใช้ดูเหมือนกับperlเวอร์ชันไม่ใช่util-linuxเวอร์ชัน
John1024

สำหรับเร็กคอร์ดนี่คือrenameหน้าคู่มือสำหรับเวอร์ชันutil-linux อย่างไรก็ตามนอกเหนือจากบันทึกย่อนั้นสิ่งที่สำคัญคือ OP ได้รับคำตอบของเขา
Cristian Ciupitu

@CristianCiupitu ขอบคุณที่ค้นพบสิ่งนั้น กลับมาหาคุณพร้อมกับ +1
John1024

1
@ John1024 archlinux แต่ฉันรู้วิธีไปpacman -S perl-renameแล้วฉันคิดว่าคุณสามารถนามแฝง
ChiseledAbs

5

ฉันจะแทนที่trคำสั่งเหล่านั้นทั้งหมดด้วยsedคำสั่งทดแทนเช่น:

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done

4

ไม่นับmvคุณไม่จำเป็นต้องมีกระบวนการภายนอกสำหรับเรื่องนี้เลย - คุณสามารถทำให้พวกเขาโง่ได้

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

ถึงกระนั้นนั่นหมายถึงการmvร้องขอต่อไฟล์และอาจrenameจะดีกว่า แม้ว่าสิ่งนี้จะทำงานได้รับเพียง POSIX mvใน$PATHและ POSIX เชลล์

ดังนั้นฉันจึงคิดแบบบ้าคลั่งสำหรับเรื่องนี้ ชุดทดสอบถูกสร้างขึ้นเช่น:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\\\%04o\\\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

ในตอนแรกฉันจะเป็นคนแรกที่ยอมรับว่าคำสั่งด้านบนสร้างผลลัพธ์ที่สามารถหาได้ง่ายขึ้นโดยวิธีการอื่น แต่วิธีการอื่นอาจไม่แสดงให้เห็นเช่นกันว่าจะทำอย่างไรกับ$IFSและจินตนาการเล็กน้อย(ป่วย)?

ดังนั้นบิตแรกนั้นค่อนข้างตรงไปตรงมา:

  • tee ไปป์ที่อินพุต 5 ชุด - เอกสารที่เรียกว่า CGEN

  • dd บล็อกอินพุตโดยบรรทัดใหม่ที่ 90 ไบต์ต่อบล็อกและไปป์ที่ ...

  • sedรวม 2 บล็อกเหล่านั้นเข้ากับ\nอักขระ ewline สองตัว'อัญประกาศผลลัพธ์เดียวและเติมสตริงtouch --สำหรับทุกรอบบรรทัดก่อนที่จะไพพ์ไปที่ ...

  • sh ซึ่งจะดำเนินการอินพุตทั้งหมดเป็นคำสั่งเชลล์

#CGENบิต แต่ ... ดีชั่วครู่ ...

  • ด้านล่างprintfพิมพ์ 252 0 วินาที

  • ถัดไปจากครั้งสุดท้ายได้รับ''อาร์กิวเมนต์สตริงโมฆะ252 และสำหรับแต่ละพิมพ์เนื้อหาของ$nตามด้วยสตริง" $i "

  • evalตีความข้อโต้แย้งของค่าถัดไปprintfก่อนที่จะพิมพ์ผลลัพธ์ของการตีความดังกล่าวเป็นตัวเลขฐานแปดที่เตรียมไว้โดย 2 เครื่องหมายแบ็กสแลชหนึ่งชิ้น

  • สุดท้ายprintfพิมพ์ค่าไบต์สำหรับ octals เหล่านั้น 2 ในเวลาตามด้วยสตริง-_ ---___สำหรับแต่ละคู่

  • $nถูกกำหนดค่าเริ่มต้นให้กับสมการที่จะเพิ่มขึ้นทีละ$iหนึ่งสำหรับการประเมินผลทุกครั้งยกเว้นว่าจะข้ามค่า 10, 39, หรือ 47 - (ซึ่งเป็น\newline, 'ใบเสนอราคาเดียวและ/เครื่องหมายทับในทศนิยม ASCII ตามลำดับ)

ผลลัพธ์ที่ได้คือไดเรกทอรีที่มีชื่อไฟล์น่าเกลียดจำนวนมากที่บรรจุทุกไบต์ในชุดอักขระของฉันตั้งแต่ 1 ถึง 255 ยกเว้นการอ้างคำเดียว (ข้ามเพื่อหลีกเลี่ยงsed s///คำสั่งเพิ่มเติมอีกหนึ่งรายการ)และ/เครื่องหมายทับ ชื่อไฟล์เหล่านั้นมีลักษณะดังนี้:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-`M-` -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

ตอนนี้ฉันจะได้รับข้อมูลเกี่ยวกับไฟล์เหล่านี้:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

เอาท์พุท

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

ตกลง. ในที่สุดการกระทำ:

ifsqz '_ -'
chksqz '_ -'

เอาท์พุท

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

ที่ประสบความสำเร็จ! คุณสามารถเห็นตัวเอง:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\\]]^^``aa??bbccddeeffgghh
]]^^``aabbc??cddeeffgghhii
^^``aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\\
RRSSTTUUVVWW??XXYYZZ[[\\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\\]]^^??``aabbccddee
ZZ[[\\]]^^``??aabbccddeeff

2
+1 สำหรับการใช้งานอย่างสร้างสรรค์ของIFS+printf
John1024

@ John1024 - มีอะไรที่สนุกจริงๆ:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
mikeserv

1
new="$(IFS=" -_"; printf %s $1)"ส้อม subshell (ยกเว้นใน ksh93) และมีปัญหากับการขึ้นบรรทัดใหม่ อีกทางเลือกหนึ่งคือการใช้IFS=' -_'; set -- $1; IFS=; new="$*"(และเปลี่ยนลูปของคุณเป็นลูปต่อไป)
Stéphane Chazelas

1
[ -e x ]จะส่งคืนค่าเท็จหากxเป็น symlink ไปยังไฟล์ที่ไม่มีอยู่หรือไม่สามารถเข้าถึงได้
Stéphane Chazelas

1
เปลือกที่ดี Kung-Fu!
ตอบโต้

2

หากคุณมี Perl คุณมักจะเปลี่ยนชื่อ คุณทำได้:

> type rename
rename is /usr/bin/rename

และแสดงวิธีเขียนสคริปต์นี้:

> cat /usr/bin/rename | head -n 5 #firt 5 lines for example
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker (Robin.Barker@npl.co.uk),
#  from Larry Wall's original script eg/rename from the perl source.
#

สคริปต์นี้ไม่รองรับแฟล็ก -i (นี่เป็นรุ่นในระบบของฉัน) แต่บางทีคุณอาจสนับสนุน อะไรคือข้อโต้แย้ง อันดับแรกคือนิพจน์ทั่วไปที่มีรูปแบบ PCRE ซึ่งทำงานเหมือนตัวกรองแก้ไขชื่ออินพุตเป็นชื่อเอาต์พุต รายการชื่ออินพุตที่คุณป้อนด้วยเครื่องหมายดอกจัน '*' ตัวอย่างเช่นคุณทำ:

> cd /tmp
> rename 's/ //g' *

ใน '*' จริงสามารถขยายไปที่:

> rename 's/ //g' file1 file2 file3 othe files found in current directory

เมื่อคุณมีจำนวนไฟล์ที่ใหญ่มากคุณจะติดกับดัก เชลล์จะขยายบรรทัดของคุณให้ยาวกว่าที่ระบบยอมรับ จากนั้นคุณสามารถแก้ไขปัญหาได้โดยใช้ find หรือ xargs การใช้ 'find' เป็นปัญหาเนื่องจากการเปลี่ยนชื่อจะถูกเรียกหลายครั้งเท่ากับจำนวนไฟล์ในไดเรกทอรี ควรใช้ xargs พร้อมกับตัวเลือก -r การโทรเปลี่ยนชื่อหนึ่งครั้งจะแก้ไขไฟล์จำนวนมาก ตัวอย่างเช่น:

> ls | xargs -r rename 's/ //g'   #thats all, names will be appended at the end of this command.

ปัญหาที่ผ่านมามันหมายความว่าอะไร:

's/ //g'

นี่เป็นนิพจน์ทั่วไปสำหรับการแก้ไขชื่อ หลังจากแรก '/' คือพื้นที่ ตรวจพบสิ่งนี้และแทนที่ด้วยสตริงหลังจาก '/' ที่สอง แต่มีสตริงว่างที่ลงท้ายด้วย '/' ที่สามจากนั้นพื้นที่จะถูกแทนที่ด้วยไม่มีอะไร ตัวเลือก 'g' ทำให้การแสดงออกซ้ำ การแสดงออกจะเดินสำหรับชื่อทั้งหมดตั้งแต่ต้นจนจบและตรวจจับช่องว่างทั้งหมด

แต่ถ้าคุณมีอักขระแท็บหรืออักขระ 'สีขาว' อื่น ๆ มีการแทนที่สำหรับ '\ s' นี้ อักขระที่ไม่จำเป็นอื่น ๆ เพียงเพิ่มลงในนิพจน์ ปิดด้วยวงเล็บเช่น:

's/[\s_-]//g'

นี่คือทั้งหมด. คุณเห็นความคล้ายคลึงกันไหม ฉันคิดว่าคุณควรอ่าน man perlrequick และ man perlretut สิ่งนี้อธิบายคุณ (ฉันหวังว่า) การแสดงออกปกติทำงานอย่างไร คุณสามารถใช้คำสั่งเปลี่ยนชื่อในสคริปต์ของคุณเองหากคุณต้องการมัน


1

shเชลล์ลูปต่อไปนี้จะลบช่องว่างขีดล่างและขีดกลางออกจากชื่อของไฟล์ในไดเรกทอรีปัจจุบันระวังอย่าเขียนทับไฟล์ที่มีอยู่:

for f in *; do
    test -f "$f" || continue
    nf=$( echo "$f" | tr -d ' _-' )
    ! test -e "$nf" && echo mv "$f" "$nf"
done

สำหรับbashและkshและเป็น verbose มากขึ้นเล็กน้อยด้วยตรรกะ:

for f in *; do
    if [[ -f "$f" ]]; then
        nf=$( tr -d ' _-' <<<"$f" )
        if [[ ! -e "$nf" ]]; then
            echo mv "$f" "$nf"
        fi
    fi
done

ลบechoเมื่อคุณแน่ใจว่าทำในสิ่งที่คุณต้องการ

trคำสั่งจะลบ ( -d) ตัวอักษรใด ๆ ในชุดที่ได้รับของตัวละคร ( ' _-') เป็นสิ่งสำคัญที่จะมีเส้นประในจุดเริ่มต้นหรือจุดจบของชุดมิฉะนั้นจะถูกตีความว่าเป็นช่วงของตัวละคร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.