ගැටලුව වන්නේ
for f in $(find .)
නොගැලපෙන කරුණු දෙකක් ඒකාබද්ධ කරයි.
findනව රේඛා අක්ෂර මගින් වෙන් කරන ලද ගොනු මාර්ග ලැයිස්තුවක් මුද්රණය කරයි. $(find .)එම ලැයිස්තු සන්දර්භය තුළ ඔබ සඳහන් නොකළ විට එය කැඳවනු ලබන භේදය + ග්ලෝබ් ක්රියාකරු $IFS(පෙරනිමියෙන් නව රේඛාව පමණක් නොව අවකාශය සහ ටැබ් (සහ එන්.යූ.එල් zsh) ද ඇතුළත් වේ. in zsh) (සහ ksh93 හෝ pdksh ව්යුත්පන්නයන්හි වරහන් ප්රසාරණය පවා!).
ඔබ එය සෑදුවත්:
IFS='
' # split on newline only
set -o noglob # disable glob (also disables brace expansion in pdksh
# but not ksh93)
for f in $(find .) # invoke split+glob
නව රේඛා අක්ෂරය ගොනු මාර්ගයක ඇති තරම් වලංගු බැවින් එය තවමත් වැරදිය. ප්රතිදානය find -printහුදෙක් විශ්වාසදායක ලෙස පශ්චාත් සැකසුම් කළ නොහැකි ය ( මෙහි පෙන්වා ඇති පරිදි සමහර කැටි ගැසුණු උපක්රම භාවිතා කිරීමෙන් හැර ).
එහි අර්ථය වන්නේ කවචයට ප්රතිදානය findසම්පුර්ණයෙන්ම ගබඩා කළ යුතු අතර , පසුව ලිපිගොනු විවර කිරීමට පෙර එය බෙදන්න + ග්ලෝබ් කරන්න (එයින් අදහස් කරන්නේ එම ප්රතිදානය දෙවන වරටත් මතකයේ ගබඩා කිරීමයි).
find . | xargs cmdසමාන ගැටළු ඇති බව සලකන්න (එහි, හිස්, නව රේඛාව, තනි උපුටා දැක්වීම, ද්විත්ව උපුටා දැක්වීම සහ බැක්ස්ලෑෂ් (සහ සමහර xargක්රියාත්මක කිරීම් සමඟ බයිට් වලංගු අක්ෂරවල කොටසක් නොවීම) ගැටළුවක් වේ)
වඩාත් නිවැරදි විකල්ප
එකක් භාවිතා කිරීමට ඇති එකම මාර්ගය වන forප්රතිදානය මත ලූප findභාවිතා කිරීමට වනු ඇත zshසහය දැක්වීම IFS=$'\0'සහ:
IFS=$'\0'
for f in $(find . -print0)
(ආදේශ -print0සමග -exec printf '%s\0' {} +සඳහා findඅද කාලයේ නොවන සම්මත (නමුත් ඉතා පොදු සහාය දෙන බව) නැහැ නිර්මාණයන් -print0).
මෙන්න, නිවැරදි හා අතේ ගෙන යා හැකි ක්රමය භාවිතා කිරීම -exec:
find . -exec something with {} \;
නැතහොත් somethingතර්ක එකකට වඩා ගත හැකි නම් :
find . -exec something with {} +
ඔබට එම ලිපිගොනු ලැයිස්තුව ෂෙල් එකකින් හැසිරවීමට අවශ්ය නම්:
find . -exec sh -c '
for file do
something < "$file"
done' find-sh {} +
(පරෙස්සම් වන්න එය එකකට වඩා ආරම්භ විය හැක sh).
සමහර පද්ධති වල, ඔබට මෙය භාවිතා කළ හැකිය:
find . -print0 | xargs -r0 something with
ඇති සාමාන්ය වාක්ය රීති හා මාධ්යයන් මත කුඩා වාසිය නමුත් somethingගේ stdinඑක්කෝ නල හෝ /dev/null.
ඔබට එය භාවිතා කිරීමට අවශ්ය විය හැකි එක් හේතුවක් වන්නේ සමාන්තර සැකසුම් සඳහා -PGNU විකල්පය භාවිතා කිරීමයි xargs. මෙම stdinප්රශ්නය ද GNU සමග පමණ වැඩ කළ හැකි xargsසමග -aෂෙල් වෙඩි ක්රියාවලිය ආදේශන සහාය සමඟ විකල්පය:
xargs -r0n 20 -P 4 -a <(find . -print0) something
නිදසුනක් ලෙස, somethingගොනු තර්ක 20 ක් ගන්නා එක් එක් සමගාමී ආයාචනා 4 ක් දක්වා ධාවනය කිරීම .
සමඟ zshහෝ bash, ප්රතිදානයට වඩා ලූපයක් ලබා ගත හැකි තවත් ක්රමයක් find -print0වන්නේ:
while IFS= read -rd '' file <&3; do
something "$file" 3<&-
done 3< <(find . -print0)
read -d '' නව රේඛා වෙන්කර ඇති ඒවා වෙනුවට NUL වෙන් කළ වාර්තා කියවයි.
bash-4.4සහ ඊට ඉහළින් ඇති ලිපිගොනු find -print0අරාවකින් ගබඩා කළ හැකිය :
readarray -td '' files < <(find . -print0)
මෙම zshසමාන (ආරක්ෂා කර ගැනීමේ වාසිය ඇති findපිටවීමේ තත්ත්වය 's):
files=(${(0)"$(find . -print0)"})
සමඟ zsh, ඔබට බොහෝ findප්රකාශන ග්ලෝබ් සුදුසුකම් සහිත පුනරාවර්තන ග්ලෝබිං සංයෝජනයකට පරිවර්තනය කළ හැකිය . නිදසුනක් ලෙස, ලිහිල් කිරීම find . -name '*.txt' -type f -mtime -1වනුයේ:
for file (./**/*.txt(ND.m-1)) cmd $file
හෝ
for file (**/*.txt(ND.m-1)) cmd -- $file
(අවශ්යතාවය පරෙස්සම් --සමග මෙන් **/*, ගොනු මාර්ග සමඟ ආරම්භ නොමැත ./, ඒ සමඟ ආරම්භ කළ හැක -උදාහරණයක්).
ksh93හා bashඅවසානයේ සඳහා සහය එක් **/(වැඩි ආවර්තනික globbing ආකාර අත්තිකාරම් ඇතත්), නමුත් භාවිතය වන පරිදි තවමත් glob සුදුසුකම් නොමැති **ඉතා එහි සීමා විය. bash4.3 ට පෙර ඩිරෙක්ටරි ගසෙන් බැසීමේදී සිම්ලින්ක් අනුගමනය කරන බවට පරිස්සම් වන්න .
පෙරළීම සඳහා මෙන් $(find .), එයින් අදහස් වන්නේ මතක ගොනු 1 හි ඇති මුළු ගොනු ලැයිස්තුවම ගබඩා කිරීමයි . සමහර අවස්ථාවලදී ලිපිගොනු පිළිබඳ ඔබේ ක්රියාවන් ලිපිගොනු සොයා ගැනීම කෙරෙහි බලපෑමක් ඇති කිරීමට ඔබ අකමැති වුවද එය යෝග්ය වේ (ඔබ සොයාගත හැකි තවත් ලිපිගොනු එකතු කරන විට වැනි).
වෙනත් විශ්වසනීයත්වය / ආරක්ෂක කරුණු
තරඟ කොන්දේසි
දැන්, අපි විශ්වසනීයත්වය ගැන කතා කරන්නේ නම්, කාලය find/ ධාවන zshගොනුව සොයා ගැනීම සහ එය නිර්ණායක හා එය භාවිතා කරන වේලාව ( TOCTOU race ) අතර ඇති ධාවන කොන්දේසි සඳහන් කළ යුතුය.
ඩිරෙක්ටරි ගසකින් බැසීමේදී පවා, සිම්ලින්ක් අනුගමනය නොකිරීමට වග බලා ගත යුතු අතර TOCTOU තරඟයකින් තොරව එය කළ යුතුය. find( findඅවම වශයෙන් GNU ) එය කරන්නේ openat()නිවැරදි O_NOFOLLOWධජ භාවිතා කරමින් නාමාවලි විවෘත කිරීමෙන් (සහය දක්වන තැන) සහ එක් එක් නාමාවලිය සඳහා ගොනු විස්තරයක් විවෘතව තබා ගැනීමෙන් zsh/ bash/ kshඑසේ නොකරන්න. එබැවින් ප්රහාරකයෙකුට නියම වේලාවට නාමාවලියක් සිම්ලින්ක් සමඟ ප්රතිස්ථාපනය කිරීමට හැකි වූ විට, ඔබට වැරදි නාමාවලියෙන් බැස යා හැකිය.
findනාමාවලිය නිසියාකාරව බැස ගියත් , -exec cmd {} \;ඊටත් වඩා වැඩි ගණනක් -exec cmd {} +එක් වරක් cmdක්රියාත්මක කළත් , නිදසුනක් ලෙස cmd ./foo/barහෝ cmd ./foo/bar ./foo/bar/bazකාලය cmdභාවිතා කරන විට ./foo/bar, ගුණාංග barතවදුරටත් ගැලපෙන නිර්ණායක සපුරාලන්නේ නැත find, නමුත් ඊටත් වඩා නරක ./fooවිය හැකිය. වෙනත් යම් ස්ථානයක් වෙත symlink වෙනුවට (සහ ජාතිය කවුළුව සමග ගොඩක් විශාල කර ඇත -exec {} +එහිදී findබොත්තමක් ඔබා එය කතා කිරීමට තරම් ගොනු කිරීමට cmd).
සමහර findක්රියාවට -execdirනැංවීමේදී (සම්මත නොවන) දෙවන ගැටළුව සමනය කිරීම සඳහා පුරෝකථනයක් ඇත.
සමඟ:
find . -execdir cmd -- {} \;
find chdir()ක්රියාත්මක වීමට පෙර ගොනුවේ මව් නාමාවලියට cmd. ඇමතීම වෙනුවට cmd -- ./foo/bar, එය අමතයි cmd -- ./bar( cmd -- barසමහර ක්රියාත්මක කිරීම් සමඟ, එබැවින් --), එබැවින් ./fooසිම්ලින්ක් වෙත වෙනස් කිරීමේ ගැටළුව මඟහරවා ගත හැකිය. එමඟින් rmආරක්ෂිත වැනි විධානයන් භාවිතා කරයි (එයට තවමත් වෙනත් ගොනුවක් ඉවත් කළ හැකිය, නමුත් වෙනත් නාමාවලියක ඇති ගොනුවක් නොවේ), නමුත් සමමුහුර්ත අනුගමනය නොකිරීමට සැලසුම් කර ඇත්නම් මිස ගොනු වෙනස් කළ හැකි විධාන නොවේ.
-execdir cmd -- {} +සමහර විට ද ක්රියා කරන නමුත් GNU findහි සමහර අනුවාදයන් ඇතුළු ක්රියාත්මක කිරීම් කිහිපයක් සමඟ එය සමාන වේ -execdir cmd -- {} \;.
-execdir ඉතා ගැඹුරු නාමාවලි ගස් හා සම්බන්ධ සමහර ගැටලු සමඟ කටයුතු කිරීමේ වාසිය ද ඇත.
තුළ:
find . -exec cmd {} \;
ලබා දී ඇති මාවතේ ප්රමාණය cmdගොනුව ඇති ඩිරෙක්ටරියේ ගැඹුර සමඟ වර්ධනය වේ. එම ප්රමාණය PATH_MAX(ලිනක්ස් හි 4k වැනි) වඩා විශාල cmdවුවහොත්, එම මාර්ගයේ සිදුවන ඕනෑම පද්ධති ඇමතුමක් ENAMETOOLONGදෝෂයකින් අසමත් වේ .
සමඟ -execdir, ගොනුවේ නම පමණක් (සමහර විට උපසර්ගය සහිතව ./) ලබා දෙනු cmdලැබේ. බොහෝ ගොනු පද්ධතිවල ඇති ගොනු නම් වලට වඩා අඩු සීමාවක් ( NAME_MAX) ඇත PATH_MAX, එබැවින් ENAMETOOLONGදෝෂය ඇතිවීමට ඇති ඉඩකඩ අඩුය.
බයිට්ස් එදිරිව අක්ෂර
බොහෝ විට findයුනික්ස් වැනි පද්ධති වල ගොනු නාමයන් බයිට් අනුක්රමයකි (ඕනෑම බයිට් අගයක් නමුත් ගොනු මාර්ගයක 0, සහ බොහෝ පද්ධතිවල) (සාමාන්යයෙන් ගොනු නාම හැසිරවීමේදී) ආරක්ෂාව ගැන සැලකිලිමත් වන විට බොහෝ විට නොසලකා හරිනු ලැබේ. ASCII මත පදනම් වූ ඒවා, අපි දැනට දුර්ලභ EBCDIC පදනම් කරගත් ඒවා නොසලකා හරිනු ඇත) 0x2f යනු මාර්ගය පරිසීමකය).
එම බයිට් පෙළ ලෙස සලකා බැලීමට අවශ්ය දැයි තීරණය කිරීම යෙදුම් සතු ය. ඔවුන් සාමාන්යයෙන් එසේ කරයි, නමුත් සාමාන්යයෙන් බයිට් වලින් අක්ෂර වලට පරිවර්තනය කිරීම පරිශීලකයාගේ ස්ථානය මත පදනම්ව පරිසරය මත පදනම් වේ.
එහි තේරුම නම්, ලබා දී ඇති ගොනු නාමයකට පෙදෙසි අනුව වෙනස් පෙළ නිරූපණයක් තිබිය හැකිය. නිදසුනක් ලෙස, බයිට් අනුක්රමය 63 f4 74 e9 2e 74 78 74වනුයේ côté.txtඅක්ෂර කට්ටලය ISO-8859-1 cєtщ.txtවන පෙදෙසක එම ලිපිගොනු නාමය අර්ථ නිරූපණය කරන යෙදුමක් සඳහා වන අතර ඒ වෙනුවට අක්ෂර කට්ටලය IS0-8859-5 වේ.
වඩාත් නරක ය. අක්ෂර කට්ටලය යූටීඑෆ් -8 (වර්තමානයේ සාමාන්යය) ඇති ප්රදේශයක, 63 f4 74 e9 2e 74 78 74 අක්ෂරවලට අනුරූපණය කළ නොහැක!
findගොනු නාම එහි -name/ -pathඅනාවැකි සඳහා පෙළක් ලෙස සලකන එවැනි යෙදුමකි (සහ තවත්, සමහර ක්රියාත්මක කිරීම් වැනි -inameහෝ වැඩි ගණනක් -regex).
එහි තේරුම නම්, උදාහරණයක් ලෙස, findක්රියාත්මක කිරීම් කිහිපයක් සමඟ (GNU ද ඇතුළුව find).
find . -name '*.txt'
63 f4 74 e9 2e 74 78 74යූටීඑෆ් -8 පෙදෙසක *( අක්ෂර 0 හෝ ඊට වැඩි ගණනක් ගැලපෙන , බයිට් නොව) එම අක්ෂර නොවන අක්ෂර සමඟ නොගැලපෙන බැවින් ඉහත අපගේ ගොනුව සොයාගත නොහැකි වනු ඇත .
LC_ALL=C find... සී පෙදෙසි අක්ෂරයකට එක් බයිට් එකක් අදහස් කරන අතර (සාමාන්යයෙන්) සියලු බයිට් අගයන් අක්ෂරයකට සිතියම් ගත කරන බවට සහතික වන බැවින් (සමහර බයිට් අගයන් සඳහා නිර්වචනය නොකළ ඒවා විය හැක).
දැන් එම ලිපිගොනු ෂෙල් එකකින් ලූප කිරීම ගැන කතා කරන විට, එම බයිට් එදිරිව අක්ෂරය ද ගැටළුවක් විය හැකිය. අපි සාමාන්යයෙන් ප්රධාන ෂෙල් වෙඩි 4 ක් දකිමු:
තවමත් බහු-බයිට් නොදන්නා අය වැනි dash. ඔවුන් සඳහා, බයිට් අක්ෂරයකට සිතියම් ගත කරයි. උදාහරණයක් ලෙස, UTF-8 හි côtéඅක්ෂර 4 ක් ඇත, නමුත් බයිට් 6 කි. යූටීඑෆ් -8 අක්ෂර වින්යාසය ඇති පෙදෙසක, දී
find . -name '????' -exec dash -c '
name=${1##*/}; echo "${#name}"' sh {} \;
findයූටීඑෆ් -8 හි කේතනය කර ඇති අක්ෂර 4 කින් සමන්විත ලිපිගොනු සාර්ථකව සොයාගනු ඇත, නමුත් dashදිග 4 ත් 24 ත් අතර දිග වාර්තා කරයි.
yash: ප්රතිවිරුද්ධ. එය ගනුදෙනු කරන්නේ චරිත සමඟ පමණි . එය ගන්නා සියලුම ආදාන අභ්යන්තරව අක්ෂර වලට පරිවර්තනය වේ. එය වඩාත් ස්ථාවර කවචයක් සාදයි, නමුත් එයින් අදහස් කරන්නේ එයට අත්තනෝමතික බයිට් අනුක්රමයන් (වලංගු අක්ෂර වලට පරිවර්තනය නොකරන) සමඟ කටයුතු කළ නොහැකි බවයි. සී පෙදෙසෙහි වුවද, 0x7f ට වඩා වැඩි බයිට් අගයන් සමඟ කටයුතු කළ නොහැක.
find . -exec yash -c 'echo "$1"' sh {} \;
côté.txtඋදාහරණයක් ලෙස UTF-8 පෙදෙසක අපගේ ISO-8859-1 මත අසමත් වනු ඇත .
බහු-බයිට් සහාය ක්රමයෙන් එකතු කර ඇති bashහෝ කැමති අය zsh. ඒවා අක්ෂර ලෙස සිතියම් ගත කළ නොහැකි බයිට් සලකා බැලීමට නැවත වැටෙනු ඇත. GBK හෝ BIG5-HKSCS වැනි අඩු බහු-බයිට් අක්ෂර කට්ටල සමඟ ඔවුන් සතුව තවමත් දෝෂ කිහිපයක් තිබේ (ඒවායේ බහු-බයිට් අක්ෂර 0-127 පරාසය තුළ (ASCII අක්ෂර වැනි) බයිට් අඩංගු බැවින් ඒවා ඉතා අශෝභන වේ. ).
වගේ අය sh(අවම වශයෙන් 11) ඒ අතරින් කිහිපයකි හෝ mksh -o utf8-modeසහාය ලබා දෙන නමුත්, UTF-8 සඳහා පමණක් බහු-බයිට්.
සටහන්
1 සම්පූර්ණත්වය සඳහා, zshසම්පූර්ණ ලැයිස්තුවම මතකයේ ගබඩා නොකර පුනරාවර්තන ග්ලෝබින් භාවිතයෙන් ලිපිගොනු ලූපයක් බවට පත්කිරීමේ ක්රමයක් අපට සඳහන් කළ හැකිය :
process() {
something with $REPLY
false
}
: **/*(ND.m-1+process)
+cmdයනු cmdවර්තමාන ගොනු මාර්ගය සමඟ (සාමාන්යයෙන් ශ්රිතයක්) කැඳවන ග්ලෝබ් සුදුසුකම්යකි $REPLY. ගොනුව තෝරා ගත යුතුද යන්න තීරණය කිරීම සඳහා ශ්රිතය සත්ය හෝ අසත්යය ලබා දෙයි (තවද අරාවෙහි ඇති $REPLYගොනු කිහිපයක් වෙනස් කිරීමට හෝ ආපසු ලබා දීමටද පුළුවන $reply). මෙන්න අපි එම ශ්රිතයේ සැකසුම් සිදු කර අසත්යය නැවත ලබා දෙන්නෙමු එවිට ගොනුව තෝරා නොගනී.