كيف يمكنني استخدام xargs لنسخ الملفات التي تحتوي أسماؤها على مسافات وعلامات اقتباس؟
-
02-07-2019 - |
سؤال
أحاول نسخ مجموعة من الملفات الموجودة أسفل الدليل ويحتوي عدد من الملفات على مسافات وعلامات اقتباس مفردة في أسمائها.عندما أحاول الخيط معًا find
و grep
مع xargs
, ، أحصل على الخطأ التالية:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
هل هناك أي اقتراحات لاستخدام أكثر قوة لـ xargs؟
هذا قيد التشغيل نظام التشغيل Mac OS X 10.5.3 (الفهد) مع BSD xargs
.
المحلول
يمكنك الجمع بين كل ذلك في واحد find
يأمر:
find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;
سيتعامل هذا مع أسماء الملفات والأدلة التي تحتوي على مسافات.يمكنك استخدام -name
للحصول على نتائج حساسة لحالة الأحرف.
ملحوظة:ال --
تم تمرير العلم إلى cp
يمنعه من معالجة الملفات بدءًا من -
كخيارات.
نصائح أخرى
find . -print0 | grep --null 'FooBar' | xargs -0 ...
لا أعرف ما إذا كان grep
يدعم --null
, ، ولا سواء xargs
يدعم -0
, ، على Leopard، ولكن على GNU كل شيء جيد.
أسهل طريقة لفعل ما يريده الملصق الأصلي هي تغيير المحدد من أي مسافة بيضاء إلى حرف نهاية السطر فقط كما يلي:
find whatever ... | xargs -d "\n" cp -t /var/tmp
يعد هذا أكثر كفاءة لأنه لا يقوم بتشغيل "cp" عدة مرات:
find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
وقعت في نفس المشكلة.وإليك كيف قمت بحلها:
find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar
إستعملت sed
لاستبدال كل سطر من الإدخال بنفس السطر، ولكن محاطًا بعلامات اقتباس مزدوجة.من sed
صفحة الرجل "...يتم استبدال علامة العطف (``&'') التي تظهر في الاستبدال بالسلسلة المطابقة لـ RE..." -- في هذه الحالة، .*
, ، الخط بأكمله.
هذا يحل xargs: unterminated quote
خطأ.
تعمل هذه الطريقة على نظام التشغيل Mac OS X الإصدار 10.7.5 (أسد):
find . | grep FooBar | xargs -I{} cp {} ~/foo/bar
لقد اختبرت أيضًا بناء الجملة الدقيق الذي نشرته.وقد نجح ذلك أيضًا على الإصدار 10.7.5.
فقط لا تستخدم xargs
.إنه برنامج أنيق لكنه لا يسير على ما يرام find
عندما تواجه حالات غير تافهة.
هنا حل محمول (POSIX)، أي.واحد لا يتطلب find
, xargs
أو cp
ملحقات جنو المحددة:
find . -name "*FooBar*" -exec sh -c 'cp -- "$@" ~/foo/bar' sh {} +
لاحظ النهاية +
بدلا من المعتاد ;
.
هذا الحل:
يتعامل بشكل صحيح مع الملفات والأدلة التي تحتوي على مسافات مضمنة أو أسطر جديدة أو أي أحرف غريبة.
يعمل على أي نظام Unix أو Linux، حتى تلك التي لا توفر مجموعة أدوات GNU.
لا يستخدم
xargs
وهو برنامج جميل ومفيد، ولكنه يتطلب الكثير من التغيير والتبديل والميزات غير القياسية للتعامل معه بشكل صحيحfind
انتاج.هو أيضا أكثر فعالية (يقرأ أسرع) من الإجابات المقبولة ومعظم الإجابات الأخرى إن لم يكن كلها.
لاحظ أيضاً أنه رغم ما ورد في بعض الردود أو التعليقات الأخرى نقلاً {}
لا فائدة منه (إلا إذا كنت تستخدم الملف الغريب fish
صدَفَة).
فكر في استخدام خيار سطر الأوامر --null لـ xargs مع خيار -print0 في البحث.
بالنسبة لأولئك الذين يعتمدون على الأوامر، بخلاف البحث، على سبيل المثال ls
:
find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar
find | perl -lne 'print quotemeta' | xargs ls -d
أعتقد أن هذا سيعمل بشكل موثوق مع أي حرف باستثناء تغذية الأسطر (وأظن أنه إذا كان لديك خلاصات أسطر في أسماء ملفاتك، فستواجه مشاكل أسوأ من هذه).إنه لا يتطلب أدوات GNU، بل يتطلب لغة Perl فقط، لذلك يجب أن يعمل إلى حد كبير في أي مكان.
لقد وجدت أن بناء الجملة التالي يعمل بشكل جيد بالنسبة لي.
find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200
في هذا المثال، أبحث عن أكبر 200 ملف يزيد حجمها عن 1,000,000 بايت في نظام الملفات المثبت على "/usr/pcapps".
يقوم سطر Perl بين "find" و"xargs" بالهروب/الاقتباس لكل فارغ بحيث يقوم "xargs" بتمرير أي اسم ملف يحتوي على فراغات مضمنة إلى "ls" كوسيطة واحدة.
انتبه إلى أن معظم الخيارات التي تمت مناقشتها في الإجابات الأخرى ليست قياسية على الأنظمة الأساسية التي لا تستخدم أدوات GNU المساعدة (Solaris، AIX، HP-UX، على سبيل المثال).انظر بوسيكس مواصفات سلوك xargs "القياسي".
أجد أيضًا أن سلوك xargs حيث يقوم بتشغيل الأمر مرة واحدة على الأقل، حتى بدون إدخال، يكون مصدر إزعاج.
لقد كتبت نسختي الخاصة من xargs (xargl) للتعامل مع مشاكل المسافات في الأسماء (فقط الأسطر الجديدة منفصلة - على الرغم من أن "العثور على ..."تعتبر مجموعة -print0' و'xargs -0' رائعة جدًا نظرًا لأن أسماء الملفات لا يمكن أن تحتوي على أحرف ASCII NUL '\0'.إن ملف xargl الخاص بي ليس مكتملاً بالقدر الذي يجب أن يكون يستحق النشر - خاصة وأن GNU لديه تسهيلات جيدة على الأقل.
باستخدام Bash (وليس POSIX) يمكنك استخدام عملية الاستبدال للحصول على السطر الحالي داخل متغير.يمكّنك هذا من استخدام علامات الاقتباس للهروب من الأحرف الخاصة:
while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
بالنسبة لي، كنت أحاول أن أفعل شيئًا مختلفًا بعض الشيء.كنت أرغب في نسخ ملفات .txt الخاصة بي إلى مجلد tmp الخاص بي.تحتوي أسماء ملفات .txt على مسافات وأحرف الفاصلة العليا.لقد نجح هذا على جهاز Mac الخاص بي.
$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/' | xargs -I{} cp -v {} ./tmp/
إذا كانت إصدارات find وxarg على نظامك لا تدعم ذلك -print0
و -0
مفاتيح التبديل (على سبيل المثال AIX find وxargs) يمكنك استخدام هذا الرمز الرائع المظهر:
find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest
هنا سوف يعتني sed بالهروب من المسافات والاقتباسات الخاصة بـ xargs.
تم اختباره على AIX 5.3
لقد قمت بإنشاء برنامج نصي صغير محمول يسمى "xargsL" حول "xargs" والذي يعالج معظم المشكلات.
على عكس xargs، يقبل xargsL اسم مسار واحد لكل سطر.قد تحتوي أسماء المسارات على أي حرف باستثناء (من الواضح) السطر الجديد أو بايت NUL.
لا يُسمح بالاقتباس أو دعمه في قائمة الملفات - قد تحتوي أسماء الملفات الخاصة بك على جميع أنواع المسافات البيضاء والخطوط المائلة العكسية والعلامات الخلفية وأحرف البدل الصدفية وما شابه - سيعمل xargsL على معالجتها كأحرف حرفية، دون حدوث أي ضرر.
كميزة إضافية، سوف يقوم xargsL بذلك لا قم بتشغيل الأمر مرة واحدة إذا لم يكن هناك إدخال!
لاحظ الفرق:
$ true | xargs echo no data
no data
$ true | xargsL echo no data # No output
سيتم تمرير أي وسيطات يتم تقديمها إلى xargsL إلى xargs.
إليك البرنامج النصي لـ POSIX Shell "xargsL":
#! /bin/sh # Line-based version of "xargs" (one pathname per line which may contain any # amount of whitespace except for newlines) with the added bonus feature that # it will not execute the command if the input file is empty. # # Version 2018.76.3 # # Copyright (c) 2018 Guenther Brunthaler. All rights reserved. # # This script is free software. # Distribution is permitted under the terms of the GPLv3. set -e trap 'test $? = 0 || echo "$0 failed!" >& 2' 0 if IFS= read -r first then { printf '%s\n' "$first" cat } | sed 's/./\\&/g' | xargs ${1+"$@"} fi
ضع البرنامج النصي في دليل ما في $PATH الخاص بك ولا تنس ذلك
$ chmod +x xargsL
البرنامج النصي هناك لجعله قابلاً للتنفيذ.
نسخة بيرل من bill_starr لن يعمل بشكل جيد مع الأسطر الجديدة المضمنة (يتعامل فقط مع المسافات).لأولئك على سبيل المثال.Solaris حيث لا تتوفر لديك أدوات GNU، قد يكون هناك إصدار أكثر اكتمالاً (باستخدام sed)...
find -type f | sed 's/./\\&/g' | xargs grep string_to_find
اضبط وسيطات البحث و grep أو الأوامر الأخرى حسب حاجتك، لكن sed سيصلح الأسطر الجديدة/المسافات/علامات التبويب المضمنة.
إستعملت إجابة بيل ستار تم تعديله قليلاً على سولاريس:
find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file
سيؤدي هذا إلى وضع علامات الاقتباس حول كل سطر.لم أستخدم الخيار "-l" على الرغم من أنه قد يساعد.
قد تحتوي قائمة الملفات التي كنت أستخدمها على "-"، ولكن ليس أسطرًا جديدة.لم أستخدم ملف الإخراج مع أي أوامر أخرى لأنني أرغب في مراجعة ما تم العثور عليه قبل أن أبدأ في حذفها على نطاق واسع عبر xargs.
لقد تعاملت مع هذا قليلاً، وبدأت أفكر في تعديل xargs، وأدركت أنه بالنسبة لنوع حالة الاستخدام التي نتحدث عنها هنا، فإن إعادة التنفيذ البسيطة في Python هي فكرة أفضل.
لسبب واحد، وجود ما يقرب من 80 سطرًا من التعليمات البرمجية لكل شيء يعني أنه من السهل معرفة ما يحدث، وإذا كان هناك حاجة إلى سلوك مختلف، فيمكنك فقط اختراقه وتحويله إلى برنامج نصي جديد في وقت أقل مما يستغرقه الحصول عليه رد في مكان ما مثل Stack Overflow.
يرى https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs و https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py.
باستخدام yargs كما هو مكتوب (وتثبيت Python 3) يمكنك كتابة:
find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar
للقيام بنسخ 203 ملفات في وقت واحد.(هنا 203 هو مجرد عنصر نائب بالطبع، واستخدام رقم غريب مثل 203 يوضح أن هذا الرقم ليس له أهمية أخرى.)
إذا كنت تريد شيئًا أسرع حقًا ودون الحاجة إلى لغة Python، فاستخدم zarcs و yargs كنماذج أولية وأعد كتابتها بلغة C++ أو C.
قد تحتاج إلى دليل grep Foobar مثل:
find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .
تحدي الإطار - أنت تسأل عن كيفية استخدام xargs.الجواب هو:لا تستخدم xargs، لأنك لا تحتاج إليه.
ال التعليق بواسطة user80168
يصف طريقة للقيام بذلك مباشرةً باستخدام cp، دون استدعاء cp لكل ملف:
find . -name '*FooBar*' -exec cp -t /tmp -- {} +
هذا يعمل بسبب:
- ال
cp -t
تسمح العلامة بإعطاء الدليل الهدف بالقرب من بدايةcp
, ، وليس قرب النهاية.منman cp
:
-t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY
ال
--
يقول العلمcp
لتفسير كل شيء بعده كاسم ملف، وليس علامة، لذلك تبدأ الملفات بـ-
أو--
لا تخلط بينcp
;ما زلت بحاجة إلى هذا لأن-
/--
يتم تفسير الشخصيات بواسطةcp
, ، بينما يتم تفسير أي أحرف خاصة أخرى بواسطة الصدفة.ال
find -exec command {} +
المتغير يفعل بشكل أساسي نفس xargs.منman find
:
-exec command {} + This variant of the -exec action runs the specified command on the selected files, but the command line is built by appending each selected file name at the end; the total number of invoca‐ matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of `{}' is allowed within the command, and (when find is being invoked from a shell) it should be quoted (for example, '{}') to protect it from interpretation by shells. The command is executed in the starting directory. If any invocation returns a non-zero value as exit status, then find returns a non-zero exit status. If find encounters an error, this can sometimes cause an immedi‐ ate exit, so some pending commands may not be run at all. This variant of -exec always returns true.
باستخدام هذا في البحث مباشرة، يؤدي هذا إلى تجنب الحاجة إلى استدعاء توجيه الإخراج أو الصدفة، بحيث لا داعي للقلق بشأن أي أحرف سيئة في أسماء الملفات.
إذا كنت تستخدم Bash، فيمكنك التحويل com.stdout إلى مجموعة من الخطوط mapfile
:
find . | grep "FooBar" | (mapfile -t; cp "${MAPFILE[@]}" ~/foobar)
الفوائد هي:
- إنه مدمج، لذا فهو أسرع.
- قم بتنفيذ الأمر مع جميع أسماء الملفات في وقت واحد، لذلك فهو أسرع.
يمكنك إلحاق وسائط أخرى بأسماء الملفات.ل
cp
, ، بامكانك ايضا:find . -name '*FooBar*' -exec cp -t ~/foobar -- {} +
ومع ذلك، بعض الأوامر لا تملك مثل هذه الميزة.
العيوب:
- ربما لا يتم قياسه بشكل جيد إذا كان هناك عدد كبير جدًا من أسماء الملفات.(الحد؟لا أعرف، لكنني اختبرت ملف قائمة بحجم 10 ميغابايت يتضمن أكثر من 10000 اسم ملف دون أي مشكلة، ضمن نظام Debian)
حسنًا...من يدري ما إذا كان Bash متاحًا على OS X؟