Jak spłaszczyć katalog w następującym formacie?
Przed: ./aaa/bbb/ccc.png
Po: ./aaa-bbb-ccc.png
./foo/bar/baz.pngi ./foo-bar-baz.pngoba istnieją. Zakładam, że nie chcesz zastąpić tego drugiego poprzednim?
Jak spłaszczyć katalog w następującym formacie?
Przed: ./aaa/bbb/ccc.png
Po: ./aaa-bbb-ccc.png
./foo/bar/baz.pngi ./foo-bar-baz.pngoba istnieją. Zakładam, że nie chcesz zastąpić tego drugiego poprzednim?
Odpowiedzi:
Ostrzeżenie: większość tych poleceń wpisałem bezpośrednio w przeglądarce. Zastrzegający lektor.
Z zsh i zmv :
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
Objaśnienie: Wzorzec **/*rekurencyjnie pasuje do wszystkich plików w podkatalogach bieżącego katalogu (nie pasuje do plików w bieżącym katalogu, ale nie trzeba ich zmieniać). Pierwsze dwie pary nawiasów to grupy, do których można odwoływać się jako $1i $2w tekście zastępczym. Ostatnia para nawiasów dodaje D kwalifikator glob, dzięki czemu pliki kropek nie są pomijane. -o -ioznacza przekazanie -iopcji, aby mvużytkownik był monitowany o zastąpienie istniejącego pliku.
Tylko z narzędziami POSIX:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
Objaśnienie: caseinstrukcja pomija katalog bieżący i podkatalogi najwyższego poziomu bieżącego katalogu. targetzawiera nazwę pliku źródłowego ( $0) z ./usuniętymi początkowymi kreskami i wszystkie ukośniki zastąpione myślnikami, a także końcowy z. Ostatnia zjest w przypadku, gdy nazwa pliku kończy się na nowej linii: w przeciwnym razie zastąpienie jej rozebrałoby ją.
Jeśli twój findnie obsługuje -exec … +(OpenBSD, patrzę na ciebie):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
W przypadku bash (lub ksh93) nie musisz wywoływać zewnętrznego polecenia, aby zamienić ukośniki na myślniki, możesz użyć rozszerzenia parametru ksh93 z konstrukcją zastępującą ciąg znaków ${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 sourceprzykładach brakuje pierwszego prezentowanego pliku / katalogu ... W końcu dowiedziałem się, dlaczego (normalnie) użyłeś _jako $0:) ... i dziękuję za wspaniałe odpowiedzi. Wiele się od nich uczę.
Chociaż są tutaj dobre odpowiedzi, uważam to za bardziej intuicyjne, w obrębie bash:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
Gdzie aaajest twój istniejący katalog? Pliki w nim zawarte zostaną przeniesione do bieżącego katalogu (pozostawiając tylko puste katalogi w aaa). Dwa wywołania do trobsługi zarówno katalogów, jak i spacji (bez logiki dla ukośników - ćwiczenie dla czytelnika).
Uważam to za bardziej intuicyjne i stosunkowo łatwe do poprawienia. Mógłbym zmienić findparametry, jeśli powiedzmy, że chcę znaleźć tylko pliki .png. Mogę zmieniać trpołączenia lub dodawać więcej w razie potrzeby. I mogę dodać echowcześniej, mvjeśli chcę wizualnie sprawdzić, czy zrobi to dobrze (powtórz bez dodatkowych, echotylko jeśli wszystko wygląda poprawnie:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
Zauważ też, że używam find aaa... a nie find .... lub find aaa/... ponieważ dwa ostatnie pozostawiłyby śmieszne artefakty w końcowej nazwie pliku.
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
Uwaga: nie będzie to działać w przypadku plików zawierających \n.
To oczywiście przenosi tylko fpliki typu ...
Jedyne zderzenia nazw mogłyby pochodzić z plików wcześniej istniejących w pwd
Testowany z tym podstawowym podzbiorem
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'
Wynikające z
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
Oto lswersja .. komentarze mile widziane. Działa w przypadku takich dziwacznych nazw plików
"j1ळ\n\001\n/j2/j3/j4/\nh h\n", ale naprawdę jestem zainteresowany wszelkimi pułapkami. Jest to bardziej ćwiczenie w „czy złośliwi mogą to lszrobić (solidnie)?”
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; }
Po prostu potokuj nazwę pliku + ścieżkę do trpolecenia.tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cplinię, aby cp -v "$FILE" "$NEWFILE"pomóc? (Również nie należy zakładać, że tylko pliki png.)
cpwiersza jest niewystarczające. Całe podejście do analizowania findwyników za pomocą forpętli jest wadliwe. Jedynymi bezpiecznymi sposobami korzystania findsą z -execlub, -print0jeśli są dostępne (mniej przenośne niż -exec). Proponuję bardziej solidną alternatywę, ale twoje pytanie jest w tej chwili niedokładne.
Bezpieczny dla spacji w nazwach plików:
#!/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
Przebiegłem z cpzamiast z mvoczywistych powodów, ale powinien dać to samo.
type find-> find is /usr/bin/findna moim komputerze. Używanie PATHw skrypcie nazwy zmiennej jest strasznym pomysłem. Ponadto twój skrypt zmienia nazwy plików ze spacjami lub ukośnikami odwrotnymi, ponieważ niewłaściwie używasz readpolecenia (wyszukaj w tej witrynie IFS read -r).
find is hashed (/bin/find), istotne jak? Różne dystrybucje są różne?
PATHzmiennej środowiskowej, z której korzysta każdy system uniksowy. Jeśli piszesz, /bin/findtwój skrypt jest faktycznie uszkodzony na każdym systemie (Gilles, moim i prawdopodobnie PO), który go nie ma /bin/find(np. B / c, w którym jest /usr/bin/find). Istotą tej PATHzmiennej środowiskowej jest, aby to nie problem.
$(pwd)jest już dostępny w zmiennej: $PWD. Ale trzeba nie bezwzględną ścieżkę tutaj: jeśli skrypt jest wywoływany z, powiedzmy, /home/timto próbuje zmienić nazwę /home/tim/some/pathna /home-tim-some-path.