क्या चुपचाप विफल होने के लिए "एमवी" बनाने का एक तरीका है?


61

mv foo* ~/bar/अगर कोई फाइल मेल नहीं कर रही है तो कमांडर इस संदेश को stderr में तैयार करता है foo*

mv: cannot stat `foo*': No such file or directory

हालाँकि, स्क्रिप्ट में मैं उस मामले पर काम कर रहा हूँ जो पूरी तरह से ठीक है, और मैं उस संदेश को हमारे लॉग से छोड़ना चाहूंगा।

क्या कोई भी अच्छा तरीका नहीं है कि mvचुप रहने को कहो, भले ही कुछ भी स्थानांतरित नहीं किया गया हो?


13
mv foo* ~/bar/ 2> /dev/null?
थॉमस निमन

1
अच्छी तरह से हाँ। मुझे लगता है कि मेरे मन में कुछ "अच्छे" थे, जैसे कुछ के लिए स्विच mv। :) लेकिन वह करेंगे, बिल्कुल।
जोनीक

3
मैंने देखा है कि कुछ mvकार्यान्वयन -qउन्हें शांत करने के लिए एक विकल्प का समर्थन करते हैं , लेकिन वह POSIX विनिर्देश का हिस्सा नहीं है mvmvGNU coreutils में, उदाहरण के लिए, है नहीं इस तरह के एक विकल्प है।
थॉमस निमन

खैर, मुझे नहीं लगता कि समस्या यह है कि "एमवी" को शांत कैसे रखा जाए, लेकिन इसे और अधिक ठीक से कैसे किया जाए। जाँच करना कि क्या कोई फ़ाइल / dir foo * है, और उपयोगकर्ता के पास लिखित अनुमति है, अंततः "mv" को निष्पादित करें, शायद?
Shuu Sh Shc

जवाबों:


46

क्या आप इसके लिए देख रहे हैं?

$ mv  file dir/
mv: cannot stat file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->

20
ध्यान दें, फिर भी मूल्य वापस! = 0, इसलिए कोई भी set -e(जिसे प्रत्येक शेल स्क्रिप्ट में उपयोग किया जाना चाहिए ) विफल हो जाएगा। आप || trueएकल कमांड के लिए चेक को निष्क्रिय करने के लिए एक जोड़ सकते हैं ।
रीटो

10
@reto का set -eउपयोग प्रत्येक शेल स्क्रिप्ट में नहीं किया जाना चाहिए, कई मामलों में यह त्रुटि प्रबंधन को और भी जटिल बनाता है, इसके बिना।
क्रिस डाउन

1
@ क्रिसडाउन मैं आपकी बात देख रहा हूं। यह आमतौर पर दो बुराइयों के बीच एक विकल्प है। लेकिन अगर संदेह में, मैं एक सफल असफलता की तुलना में एक असफल सफल रन पसंद करता हूं। (जो ग्राहक / उपयोगकर्ता को दिखाई नहीं दे रहा है)। शेल स्क्रिप्ट शुरुआती लोगों के लिए एक बड़ा खान क्षेत्र है, एक त्रुटि को याद करना बहुत आसान है और आपकी स्क्रिप्ट हाइअरवायर हो जाती है!
रेट

14

वास्तव में, मुझे नहीं लगता कि म्यूटिंग mvएक अच्छा तरीका है (याद रखें कि यह आपको अन्य चीजों पर भी रिपोर्ट कर सकता है जो ब्याज की हो सकती हैं ... जैसे कि गायब है ~/bar)। आप इसे केवल उसी स्थिति में म्यूट करना चाहते हैं जब आपकी ग्लोब एक्सप्रेशन परिणाम वापस नहीं करता है। वास्तव में नहीं बल्कि इसे निष्पादित करें।

[ -n "$(shopt -s nullglob; echo foo*)" ] && mv foo* ~/bar/

बहुत आकर्षक नहीं लगता है, और केवल में काम करता है bash

या

[ 'foo*' = "$(echo foo*)" ] || mv foo* ~/bar/

केवल छोड़कर आप में हैं bashके साथ nullglobसेट। आप हालांकि ग्लोब पैटर्न के 3x पुनरावृत्ति की कीमत का भुगतान करते हैं।


दूसरे रूप के साथ मामूली समस्या: यह एक फाइल को स्थानांतरित करने में विफल रहता है जिसका नाम foo*वर्तमान निर्देशिका में केवल एक है जो ग्लोब से मेल खाता है foo*। यह एक उदास अभिव्यक्ति के साथ चारों ओर काम किया जा सकता है जो खुद को शाब्दिक, कठिन से मेल नहीं खाता है। जैसे [ 'fo[o]*' = "$(echo fo[o]*)" ] || mv fo[o]* ~/bar/
फ्रा-सान

13

find . -maxdepth 1 -name 'foo*' -type f -print0 | xargs -0r mv -t ~/bar/

- GNU का mvअच्छा "डेस्टिनेशन फर्स्ट" विकल्प है ( -t) और xargsइसके कमांड को रन करना छोड़ सकता है अगर कोई इनपुट नहीं है ( -r)। का उपयोग करते हुए -print0और -0तदनुसार यकीन है कि एक गड़बड़ वहाँ नहीं होगा जब फ़ाइल नाम रिक्त स्थान और अन्य "हास्यास्पद" सामान शामिल करता है।


2
ध्यान दें कि -maxdepth, -print0, -0और -rभी जीएनयू एक्सटेंशन हैं (हालांकि उनमें से कुछ अन्य कार्यान्वयन में आजकल पाए जाते हैं)।
स्टीफन चेज़लस

1
Poige, हमारे बहुत से उपयोगकर्ता लिनक्स का उपयोग नहीं करते हैं। यह यहां मानक अभ्यास है, और बहुत उपयोगी है, यह इंगित करने के लिए कि उत्तर के कौन से हिस्से पोर्टेबल नहीं हैं। साइट को " यूनिक्स और लिनक्स" कहा जाता है , और कई लोग बीएसडी या यूनिक्स या एआईएक्स या मैकओएस या एम्बेडेड सिस्टम के कुछ रूप का उपयोग करते हैं। इसे व्यक्तिगत रूप से न लें।
terdon

मैं व्यक्तिगत तौर पर कुछ नहीं ले रहा हूं। हालाँकि, मेरे पास एक राय है जिसे मैंने अभी देखा है। इसलिए मैं दोहराता हूं: मुझे लगता है कि वे टिप्पणियां "यह ग्नू है" बहुत ही बेकार है। वे बिल्कुल भी जोड़ने के लायक नहीं हैं, सबसे पहले मेरे जवाब से स्पष्ट संकेत है कि यह जीएनयू संस्करण पर आधारित है mv
11

5

यह महसूस करना महत्वपूर्ण है कि यह वास्तव में शेल है जो कि foo*फ़ाइल नामों के मिलान की सूची में फैलता है , इसलिए थोड़ा mvही कर सकता है।

यहाँ समस्या यह है कि जब एक ग्लोब मेल नहीं खाता है, तो कुछ गोले bash(और अधिकांश अन्य बॉर्न-जैसे गोले, कि बुग्गी व्यवहार वास्तव में 70 के दशक के अंत में बॉर्न शेल द्वारा पेश किया गया था) कमांड को पैटर्न वर्बेटिम पास करते हैं।

इसलिए यहाँ, जब foo*कमांड को निरस्त करने के बजाय किसी भी फाइल से मेल नहीं खाता है (जैसे पूर्व बॉर्न शेल और कई आधुनिक गोले करते हैं), शेल एक वर्बेटिम foo*फ़ाइल को पास करता है mv, इसलिए मूल रूप mvसे कॉल की गई फ़ाइल को स्थानांतरित करने के लिए कहता है foo*

वह फ़ाइल मौजूद नहीं है। यदि ऐसा होता है, तो यह वास्तव में पैटर्न से मेल खाता है, इसलिए mvएक त्रुटि की रिपोर्ट करता है। यदि पैटर्न foo[xy]इसके बजाय था, mvतो गलती से फ़ाइल और फ़ाइलों के foo[xy]बजाय एक फ़ाइल को स्थानांतरित कर सकता था ।fooxfooy

अब, उन गोले में भी, जिसमें वह समस्या नहीं है (पूर्व-बॉर्न, csh, tsh, मछली, zsh, bash -O failglob), फिर भी आपको एक त्रुटि मिलेगी mv foo* ~/bar, लेकिन इस बार शेल द्वारा।

यदि आप इस पर विचार करना चाहते हैं कि कहीं कोई फ़ाइल मिलान न हो foo*और उस स्थिति में, कुछ भी स्थानांतरित न हो, तो आप पहले फ़ाइलों की सूची बनाना चाहेंगे (एक तरह से जिसमें त्रुटि का कारण न हो जैसे nullglobविकल्प का उपयोग करके कुछ गोले), और उसके बाद केवल कॉल mvसूची गैर-रिक्त है।

यह (के रूप में जोड़ना ) की सभी त्रुटियों को छिपाने से बेहतर होगा जैसे कि किसी अन्य कारण से विफल हो जाता है, आप शायद अभी भी जानना चाहते हैं कि क्यों।mv2> /dev/nullmv

zsh में

files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/

या एक अस्थायी चर का उपयोग करने से बचने के लिए एक अनाम फ़ंक्शन का उपयोग करें:

() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)

zshउन गोले में से एक है जिनके पास बॉर्न बग नहीं है और एक कमांड को निष्पादित किए बिना एक त्रुटि की रिपोर्ट करते हैं जब एक ग्लोब मेल नहीं खाता (और nullglobविकल्प सक्षम नहीं किया गया है), तो यहां, आप zshत्रुटि को छिपा सकते हैं और पुनर्स्थापित कर सकते हैं stderr के लिए mvताकि आप अभी भी देखना होगा mvत्रुटियों यदि कोई हो, लेकिन नहीं मेल नहीं खाने वाले globs के बारे में त्रुटि:

(mv 2>&3 foo* ~/bar/) 3>&2 2>&-

या आप उपयोग कर सकते हैं zargsजो समस्याओं से भी foo*बचेंगे यदि ग्लोब बहुत आदमी फ़ाइलों तक विस्तारित होगा।

autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option

Ksh93 में:

files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/

बैश में:

bashnullglobकेवल एक ग्लोब के लिए सक्षम करने के लिए कोई सिंटैक्स नहीं है , और failglobविकल्प रद्द nullglobकर देता है ताकि आपको निम्न चीजों की आवश्यकता हो:

saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"

या बचाने के लिए एक उपधारा में विकल्प सेट करने के लिए पहले उन्हें बचाने के लिए और बाद में उन्हें बहाल करने के लिए है।

(
  shopt -s nullglob
  shopt -u failglob
  files=(foo*)
  ((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)

में yash

(
  set -o nullglob
  files=(foo*)
  [ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)

में fish

मछली के खोल में, नलग्लोब व्यवहार setकमांड के लिए डिफ़ॉल्ट है , इसलिए यह बस है:

set files foo*
count $files > /dev/null; and mv -- $files ~/bar/

POSIXly

nullglobPOSIX में कोई विकल्प नहीं है और स्थितिगत shमापदंडों के अलावा कोई सरणी नहीं है। एक ट्रिक है जिसका उपयोग आप यह पता लगाने के लिए कर सकते हैं कि एक ग्लोब मेल खाता है या नहीं:

set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
  shift
  mv -- "$@" ~/bar/
fi

दोनों foo[*]और foo*ग्लोब का उपयोग करके , हम उस मामले के बीच अंतर कर सकते हैं जहां कोई मिलान फ़ाइल नहीं है और जिस पर एक फ़ाइल है जिसे कॉल किया जाता है foo*(जो set -- foo*ऐसा नहीं कर सकता)।

अधिक पढ़ने:


3

यह शायद सबसे अच्छा नहीं है, लेकिन आप यह findजांचने के लिए कमांड का उपयोग कर सकते हैं कि फ़ोल्डर खाली है या नहीं:

find "foo*" -type f -exec mv {} ~/bar/ \;

1
यह एक शाब्दिक foo*खोज का रास्ता देगा find
Kusalananda

3

मैं मान रहा हूं कि आप बैश का उपयोग कर रहे हैं, क्योंकि यह त्रुटि बेश के व्यवहार पर निर्भर करती है कि वह अपने आप में बेजोड़ ग्लब्स का विस्तार कर ले। (तुलना करके, zsh एक बेजोड़ ग्लोब का विस्तार करने की कोशिश करते समय एक त्रुटि उठाता है।)

तो, निम्नलिखित वर्कअराउंड के बारे में क्या?

ls -d foo* >/dev/null 2>&1 && mv foo* ~/bar/

यह चुपचाप mvअगर ls -d foo*विफल रहता है, जबकि अभी भी त्रुटि लॉगिंग विफल रहता है, ls foo*लेकिन mvविफल रहता है पर ध्यान नहीं देगा। (खबरदार, मौजूदा ls foo*कारणों से अन्य कारणों से विफल हो सकता है foo*, उदाहरण के लिए, अपर्याप्त अधिकार, एफएस के साथ समस्या, आदि, इसलिए इस स्थिति को इस समाधान द्वारा चुपचाप अनदेखा कर दिया जाएगा।)


मुझे ज्यादातर बाश में दिलचस्पी है, हाँ (और मैंने सवाल को टैग किया है bash)। इस तथ्य को ध्यान में रखने के लिए +1 जो मौजूदा mvकारणों से अन्य कारणों से विफल हो सकता है foo*
जोनीक

1
(व्यवहार में मैं संभवतः इसका उपयोग नहीं करूंगा क्योंकि यह क्रिया की तरह है, और lsकमांड का बिंदु भविष्य के पाठकों के लिए बहुत स्पष्ट नहीं होगा, कम से कम एक टिप्पणी के बिना।)
जोनिक

ls -d foo*अन्य कारणों से गैर-शून्य निकास स्थिति के साथ वापस आ सकता है, जैसे कि ln -s /nowhere foobar(कम से कम कुछ lsकार्यान्वयन के बाद)।
स्टीफन चेजलस

3

आप उदाहरण के लिए कर सकते हैं

mv 1>/dev/null 2>&1 foo* ~/bar/ या mv foo* ~/bar/ 1&>2

अधिक जानकारी के लिए देखें: http://mywiki.wooledge.org/BashFAQ/055


mv foo* ~/bar/ 1&>2कमांड को मौन नहीं करता है, यह भेजता है जो stdout पर होता है वह stderr पर भी होता है।
क्रिस डाउन

और आप खिड़कियां (मेरे जैसे) के तहत MinGW उपयोग कर रहे हैं की जगह /dev/nullके साथNUL
Rian सैंडरसन

यह कमांड कैसे काम करता है? एम्परसेंड क्या करते हैं? क्या 1और क्या 2मतलब है?
हारून फ्रेंक

@AaronFranke 1 स्टैडआउट है और 2 जब एक लिनक्स प्रक्रिया बनती है तो डिफ़ॉल्ट रूप से स्टैडर पाइप होती है। लिनक्स कमांड में पाइप पर अधिक जानें। आप यहां शुरू कर सकते हैं - digitalocean.com/community/tutorials/…
मिथुन बी

2

आप (आंशिक रूप से) के साथ धोखा कर सकते हैं perl:

perl -e 'system "mv foo* ~/bar/" if glob "foo*"'

कृपया नीचे टिप्पणी करने से पहले टिप्पणी करें।
जोसेफ आर।

2

यदि आप पर्ल का उपयोग करने जा रहे हैं, तो आप सभी तरह से जा सकते हैं:

#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;

my $target = "$ENV{HOME}/bar/";

foreach my $file (<foo*>) {
    move $file, $target  or warn "Error moving $file to $target: $!\n";
}

या एक-लाइनर के रूप में:

perl -MFile::Copy -E 'move $_, "$ENV{HOME}/bar/" or warn "$_: $!\n" for <foo*>'

( moveकमांड के विवरण के लिए , फ़ाइल :: कॉपी के लिए प्रलेखन देखें ।)


-1

बजाय

mv foo* ~/bar/

तुम कर सकते हो

cp foo* ~/bar/
rm foo* 

सरल, पठनीय :)


6
नकल करना, फिर हटाना, एक सरल चाल से अधिक समय लेता है। यह भी काम नहीं करेगा यदि कोई फ़ाइल उपलब्ध मुक्त डिस्क स्थान से बड़ी है।
एंथन

11
ओपी एक समाधान चाहता है जो चुप हो। अगर मौजूद नहीं है तो cpन तो rmचुप foo*हैं।
रॉन रोथमैन

-1
mv foo* ~/bar/ 2>/dev/null

उपरोक्त कमांड सफल है या नहीं, हम पिछले कमांड के एग्जिट स्टेटस से पता लगा सकते हैं

command: echo $?

अगर इको $ का आउटपुट? 0 से अधिक का अर्थ है कमांड अनसक्सेसफुल अगर आउटपुट 0 है तो कमांड सफल है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.