ฉันจะแบนไดเรกทอรีในรูปแบบต่อไปนี้ได้อย่างไร
ก่อน: ./aaa/bbb/ccc.png
หลังจาก: ./aaa-bbb-ccc.png
./foo/bar/baz.png
และ./foo-bar-baz.png
ทั้งที่มีอยู่ ฉันคิดว่าคุณไม่ต้องการแทนที่หลังด้วยอดีตหรือไม่
ฉันจะแบนไดเรกทอรีในรูปแบบต่อไปนี้ได้อย่างไร
ก่อน: ./aaa/bbb/ccc.png
หลังจาก: ./aaa-bbb-ccc.png
./foo/bar/baz.png
และ./foo-bar-baz.png
ทั้งที่มีอยู่ ฉันคิดว่าคุณไม่ต้องการแทนที่หลังด้วยอดีตหรือไม่
คำตอบ:
คำเตือน: ฉันพิมพ์คำสั่งเหล่านี้ส่วนใหญ่โดยตรงในเบราว์เซอร์ของฉัน ตัวอักษร Caveat
ด้วย zsh และzmv :
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
คำอธิบาย: รูปแบบ**/*
ตรงกับไฟล์ทั้งหมดในไดเรกทอรีย่อยของไดเรกทอรีปัจจุบันซ้ำ (ไม่ตรงกับไฟล์ในไดเรกทอรีปัจจุบัน แต่ไม่จำเป็นต้องเปลี่ยนชื่อเหล่านี้) วงเล็บสองคู่แรกคือกลุ่มที่สามารถอ้างถึงเป็น$1
และ$2
ในข้อความแทนที่ เครื่องหมายวงเล็บคู่สุดท้ายจะเพิ่มตัวระบุแบบD
กลมเพื่อไม่ให้มองเห็นไฟล์จุด -o -i
หมายถึงการส่ง-i
ตัวเลือกเพื่อmv
ให้คุณได้รับแจ้งว่าไฟล์ที่มีอยู่จะถูกเขียนทับ
ด้วยเครื่องมือ POSIX เท่านั้น:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
คำอธิบาย: case
คำสั่งจะละเว้นไดเรกทอรีปัจจุบันและไดเรกทอรีย่อยระดับบนสุดของไดเรกทอรีปัจจุบัน target
มีชื่อแหล่งแฟ้ม ( $0
) กับชั้นนำปล้นและทับทั้งหมดแทนที่ด้วยขีดกลางบวกเป็นครั้งสุดท้าย./
z
สุดท้ายz
คือในกรณีที่ชื่อไฟล์ลงท้ายด้วยการขึ้นบรรทัดใหม่: มิฉะนั้นการทดแทนคำสั่งจะตัดมัน
หากคุณfind
ไม่สนับสนุน-exec … +
(OpenBSD ฉันกำลังมองคุณอยู่):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
ด้วย bash (หรือ ksh93) คุณไม่จำเป็นต้องเรียกคำสั่งภายนอกเพื่อแทนที่เครื่องหมายทับด้วยเครื่องหมายขีดกลางคุณสามารถใช้การขยายพารามิเตอร์ ksh93 ด้วยการสร้างสตริงแทนที่${VAR//STRING/REPLACEMENT}
:
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
for source
ตัวอย่างพลาดนำเสนอครั้งแรกของไฟล์ / dir ... ฉันได้พบในที่สุดว่าทำไมคุณ (ปกติ) ได้ใช้_
เป็น$0
:) ... และขอขอบคุณสำหรับคำตอบที่ดี ฉันเรียนรู้มากมายจากพวกเขา
ในขณะที่มีคำตอบที่ดีอยู่ที่นี่ฉันพบว่าใช้งานง่ายกว่านี้ภายในbash
:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
ที่ไหนaaa
เป็นไดเรกทอรีที่คุณมีอยู่ ไฟล์ที่มีจะถูกย้ายไปยังไดเรกทอรีปัจจุบัน (ทิ้งเฉพาะไดเรกทอรีว่างใน aaa) การเรียกทั้งสองเพื่อtr
จัดการทั้งไดเรกทอรีและช่องว่าง (โดยไม่มีตรรกะสำหรับเครื่องหมายทับ (Escape) - แบบฝึกหัดสำหรับผู้อ่าน)
ฉันคิดว่ามันใช้งานง่ายและปรับแต่งได้ง่ายกว่า นั่นคือฉันสามารถเปลี่ยนfind
พารามิเตอร์ได้ถ้าพูดว่าฉันต้องการค้นหาไฟล์. png เท่านั้น ฉันสามารถเปลี่ยนการtr
โทรหรือเพิ่มได้มากขึ้นตามต้องการ และฉันสามารถเพิ่มecho
ก่อนmv
ถ้าฉันต้องการตรวจสอบด้วยสายตาว่ามันจะทำสิ่งที่ถูกต้อง (จากนั้นทำซ้ำโดยไม่ต้องเสริมecho
เฉพาะในกรณีที่สิ่งที่ถูกต้อง:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
หมายเหตุด้วยฉันใช้find aaa
... และไม่find .
... หรือfind aaa/
... เนื่องจากสองรายการหลังจะทิ้งสิ่งประดิษฐ์ที่ตลกไว้ในชื่อไฟล์สุดท้าย
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
หมายเหตุ: \n
นี้จะไม่ทำงานสำหรับชื่อไฟล์ที่มี
แน่นอนว่านี่จะย้ายf
ไฟล์ประเภทเท่านั้น...
การปะทะกันของชื่อจะมาจากไฟล์ที่มีอยู่แล้วในpwd
ทดสอบกับเซตย่อยพื้นฐานนี้
rm -fr junk
rm -f junk*hello*
mkdir -p junk/junkier/junkiest
touch 'hello hello'
touch 'junk/hello hello'
touch 'junk/junkier/hello hello'
touch 'junk/junkier/junkiest/hello hello'
ที่เกิดขึ้นใน
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
นี่คือls
เวอร์ชั่น .. ยินดีต้อนรับความคิดเห็น มันใช้งานได้กับชื่อไฟล์ที่แปลกประหลาดเช่นนี้
"j1ळ\n\001\n/j2/j3/j4/\nh h\n"
แต่ฉันสนใจที่จะรู้ถึงข้อผิดพลาดใด ๆ มันเป็นแบบฝึกหัดมากกว่าในเรื่อง "ผู้ls
กระทำผิดจริงสามารถทำได้ (แน่นหนา) หรือไม่?"
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
เพียงไพพ์ชื่อไฟล์ + พา ธ ไปยังtr
คำสั่งtr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cp
สายเพื่อcp -v "$FILE" "$NEWFILE"
ช่วย (นอกจากนี้คุณไม่ควรถือว่าไฟล์ png เท่านั้น)
cp
บรรทัดไม่เพียงพอ วิธีการแยกวิเคราะห์find
เอาต์พุตทั้งหมดด้วยfor
ลูปนั้นมีข้อบกพร่อง วิธีเดียวที่จะปลอดภัยในการใช้งานfind
ด้วย-exec
หรือ-print0
ถ้ามี (น้อยกว่าแบบพกพา-exec
) ฉันขอแนะนำทางเลือกที่มีประสิทธิภาพมากขึ้น แต่คำถามของคุณไม่ได้รับการระบุในขณะนี้
ปลอดภัยสำหรับช่องว่างในชื่อไฟล์:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
ใช้ระเบิดcp
แทนmv
ด้วยเหตุผลที่ชัดเจน แต่ควรผลิตเหมือนกัน
type find
-> find is /usr/bin/find
บนเครื่องของฉัน การใช้PATH
เป็นชื่อตัวแปรในสคริปต์เป็นความคิดที่แย่มาก นอกจากนี้สคริปต์ชื่อไฟล์ของคุณจะมีช่องว่างหรือแบ็กสแลชเนื่องจากคุณใช้read
คำสั่งในทางที่ผิด(ค้นหาไซต์นี้IFS read -r
)
find is hashed (/bin/find)
เกี่ยวข้องอย่างไร distros ต่างกันหรือไม่
PATH
ตัวแปรสภาพแวดล้อมซึ่งเป็นสิ่งที่ทุก Unix ใช้ระบบ หากคุณเขียน/bin/find
แล้วสคริปต์ของคุณจะเสียจริง ๆ ในทุกระบบ (Gilles, Mine และอาจเป็น OP) ที่ไม่มี/bin/find
(เช่น b / c มันอยู่/usr/bin/find
) จุดรวมของPATH
ตัวแปรสภาพแวดล้อมคือการทำให้สิ่งนี้ไม่ใช่ปัญหา
$(pwd)
$PWD
แต่คุณต้องไม่ใช้เส้นทางที่แน่นอนที่นี่: ถ้าสคริปต์ของคุณจะถูกเรียกจากการพูด/home/tim
ก็พยายามที่จะเปลี่ยนชื่อไป/home/tim/some/path
/home-tim-some-path