
ファイル名でスペース文字 " "、感嘆符 "
!
"、およびドル記号 " $
"を検索し、各文字を下線 " _
"に置き換えるスクリプトがあります。ただし、感嘆符付きのファイル名はまったく処理されません。現在のフォルダとすべてのサブフォルダから名前を変更するファイルを特定の深さまで検索します(ここではあまり関係ありません)。フォルダ名も同じように変更する必要があり、これがディープインターリーブが機能する理由です。
必須の名前変更スキームは次のとおりです。
This is cool!.txt --> This_is_cool_.txt
Thank$.log --> Thank_.log
Foo 1/Bar 2/a.txt --> Foo_1/Bar_2/a.txt
スクリプトは次のとおりです。
#!/bin/tcsh -f
foreach n ( 1 2 3 4 5 6 7 8 )
find . -mindepth $n -maxdepth $n -name '*[ $\!]*' | fgrep -v \" | \
awk '{printf "mv -i -- \"%s\" \"%s\"\n", gensub("!","\\\\\!","g",$0), gensub(" ","_","g",gensub("\\$","\\\\$","g",gensub("!","\\\\\!","g",$0)))}' | tcsh -cf
end
\
2つの感嘆符の前のバックスラッシュの数を変更しましたが、効果はありません。エラーメッセージは次のとおりです(バックスラッシュの数が偶数)。
awk: cmd. line:1: warning: escape sequence `\!' treated as plain `!'
それとも(明らかに)対話型シェルを開くシェルスクリプトですか? !時には何も起こりません(mv
最初の引数にバックスラッシュが多すぎてファイルが見つからない場合のようです)。
PS:見てわかるように、"
二重引用符 ""を含むファイルも機能できないため除外しました。これらの問題を解決する方法も提案してください。
答え1
mv
コマンドを合成してawk
シェルにパイプするよりも直接呼び出す方が簡単です。
#!/bin/bash
#
find -mindepth 1 -maxdepth 8 -type f -name '*[ !$]*' -execdir
bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +
このバージョンはファイル名を変更しますが、ディレクトリ名は変更しません(質問に記載されているように)。実際にディレクトリ名を変更するには、このバリアントを使用してください。
find -mindepth 1 -maxdepth 8 -depth -name '*[ !$]*' -execdir
bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +
どちらの場合も、echo
コマンドが期待どおりに実行されることに満足している場合は、コマンドを削除してください。対話型の名前変更に使用されますmv -i
。下線はbash -c '...' _ {}
サブシェルで実行されるコードの「名前」になります。これはマッピングされ、$0
コードエラー(存在する場合)を報告するために使用されるため、bash
そこに配置できます。ただsubshell
ラベルだけです。
答え2
このスクリプトは深さを制限せずにすべてのfind
文字$!"'
を_
。
質問には特定のバージョンのオペレーティングシステムやツールに関する情報が含まれていないため、POSIX仕様にのみ依存しており、GNUなどの特定のバージョンは要求しないように努めました。
#!/bin/sh
find . -depth -name '*[ $!"]*' | while IFS= read -r file
do
dir="${file%/*}"
newname="$(echo "${file##*/}" | tr ' $!"' '____')"
echo mv "$file" "$dir/$newname"
done
このスクリプトは軽くテストされました。正しいmv
コマンドが印刷されたら削除してくださいecho
。
説明する:
find . -depth
...ファイルがディレクトリの前に印刷されていることを確認してください(両方とも置換する文字が含まれている場合)。このようにして、最初にファイル名を変更し、次に親ディレクトリの名前を変更します。
``で区切られた親ディレクトリ名はdir="${file%/*}
変更されません。
ファイル名(または最後のディレクトリ名)が抽出されましたnewname="$(echo "${file##*/}" | tr ' $!"' '____')"
。
さらなる改善
ファイル名の他の「奇妙な」文字を処理するには、次のようにタスクからfind
直接ループ本文のコードを呼び出すことをお勧めします。-exec
ロエマ~の回答。
find . -depth -name '*[ $!"]*' -exec sh -c 'for file in "$@" ; do dir="${file%/*}" ; newname="$(echo "${file##*/}" | tr '\'' $!"'\'' "____")" ; echo mv "$file" "$dir/$newname"; done' _ {} +
また、見ることができますhttps://mywiki.wooledge.org/BashFAQ/020
他の回答でasを使用すると、-execdir
ファイル名と親ディレクトリが分離されるのを防ぐことができますが、POSIXはこれを定義しません。
echo
...組み合わせなどのシェルと交換できます。|tr
"${newname//[ $!\"]/_}"