แผ่แบน แต่เก็บรักษาชื่อไดเรกทอรีในชื่อไฟล์ใหม่


11

ฉันจะแบนไดเรกทอรีในรูปแบบต่อไปนี้ได้อย่างไร

ก่อน: ./aaa/bbb/ccc.png

หลังจาก: ./aaa-bbb-ccc.png


PS: โปรดบัญชีสำหรับชื่อไฟล์ที่มีตัวอักษรแปลก ๆ (เช่นช่องว่าง)
นาธาน JB

3
นอกจากนี้คุณยังจะต้องระบุว่าคุณต้องการที่จะจัดการกับกรณีที่เป็นไปได้ที่./foo/bar/baz.pngและ./foo-bar-baz.pngทั้งที่มีอยู่ ฉันคิดว่าคุณไม่ต้องการแทนที่หลังด้วยอดีตหรือไม่
jw013

ในกรณีของฉันมันไม่เกี่ยวข้อง แต่คำตอบควรคำนึงถึงเรื่องนั้นเพื่อให้คนอื่น ๆ สามารถพึ่งพาได้! ขอบคุณ!
นาธาน JB

คำตอบ:


7

คำเตือน: ฉันพิมพ์คำสั่งเหล่านี้ส่วนใหญ่โดยตรงในเบราว์เซอร์ของฉัน ตัวอักษร 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:) ... และขอขอบคุณสำหรับคำตอบที่ดี ฉันเรียนรู้มากมายจากพวกเขา
Peter.O

โปรดทราบว่าตัวอย่าง zmv ทำเพียงแค่การรันแบบแห้ง: มันพิมพ์คำสั่งย้าย แต่มันไม่ได้รัน หากต้องการดำเนินการคำสั่งเหล่านั้นให้ลบ n ใน -Qn
ปีเตอร์

5

ในขณะที่มีคำตอบที่ดีอยู่ที่นี่ฉันพบว่าใช้งานง่ายกว่านี้ภายใน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/... เนื่องจากสองรายการหลังจะทิ้งสิ่งประดิษฐ์ที่ตลกไว้ในชื่อไฟล์สุดท้าย


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

2
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

0

นี่คือ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; }

-1

เพียงไพพ์ชื่อไฟล์ + พา ธ ไปยังtrคำสั่งtr <replace> <with>

for FILE in `find aaa -name "*.png"`
do
  NEWFILE=`echo $FILE | tr '/' '-'`
  cp -v $FILE $NEWFILE
done

1
นี่ไม่ได้จัดการกับชื่อไฟล์แปลก ๆ อย่างปลอดภัย ช่องว่างจะทำลายมัน (เหนือสิ่งอื่นใด)
jw013

จากภาพรวมครั้งแรกนี่เป็นคำตอบที่สะอาดอ่านง่าย แต่ @ jw013 ถูกต้องแล้วจะไม่รองรับช่องว่างได้ดี จะเปลี่ยนcpสายเพื่อcp -v "$FILE" "$NEWFILE"ช่วย (นอกจากนี้คุณไม่ควรถือว่าไฟล์ png เท่านั้น)
นาธาน JB

2
@ NathanJ.Brauer การเพิ่มคำพูดในcpบรรทัดไม่เพียงพอ วิธีการแยกวิเคราะห์findเอาต์พุตทั้งหมดด้วยforลูปนั้นมีข้อบกพร่อง วิธีเดียวที่จะปลอดภัยในการใช้งานfindด้วย-execหรือ-print0ถ้ามี (น้อยกว่าแบบพกพา-exec) ฉันขอแนะนำทางเลือกที่มีประสิทธิภาพมากขึ้น แต่คำถามของคุณไม่ได้รับการระบุในขณะนี้
jw013

-2

ปลอดภัยสำหรับช่องว่างในชื่อไฟล์:

#!/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ด้วยเหตุผลที่ชัดเจน แต่ควรผลิตเหมือนกัน


3
type find-> find is /usr/bin/findบนเครื่องของฉัน การใช้PATHเป็นชื่อตัวแปรในสคริปต์เป็นความคิดที่แย่มาก นอกจากนี้สคริปต์ชื่อไฟล์ของคุณจะมีช่องว่างหรือแบ็กสแลชเนื่องจากคุณใช้readคำสั่งในทางที่ผิด(ค้นหาไซต์นี้IFS read -r)
Gilles 'หยุดความชั่วร้าย' ใน

find is hashed (/bin/find)เกี่ยวข้องอย่างไร distros ต่างกันหรือไม่
ทิม

รู้ว่าฉันควรจะอยู่ห่างจากนี้เหยื่ออีแร้ง
ทิม

@Tim คุณสามารถลบคำตอบเพื่อรับตัวแทนของคุณ (และของฉันเช่นกัน b / c มีค่าใช้จ่ายตัวแทนเพื่อ downvote) การเข้ารหัสที่ยากเส้นทางเข้าไปในสคริปต์ดูเหมือนว่าจะแนะนำว่าคุณจะหายไปจุดของPATHตัวแปรสภาพแวดล้อมซึ่งเป็นสิ่งที่ทุก Unix ใช้ระบบ หากคุณเขียน/bin/findแล้วสคริปต์ของคุณจะเสียจริง ๆ ในทุกระบบ (Gilles, Mine และอาจเป็น OP) ที่ไม่มี/bin/find(เช่น b / c มันอยู่/usr/bin/find) จุดรวมของPATHตัวแปรสภาพแวดล้อมคือการทำให้สิ่งนี้ไม่ใช่ปัญหา
jw013

โดยวิธีการที่มีอยู่แล้วในตัวแปร:$(pwd) $PWDแต่คุณต้องไม่ใช้เส้นทางที่แน่นอนที่นี่: ถ้าสคริปต์ของคุณจะถูกเรียกจากการพูด/home/timก็พยายามที่จะเปลี่ยนชื่อไป/home/tim/some/path /home-tim-some-path
Gilles 'หยุดความชั่วร้าย' ใน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.