ফাঁকা স্থান সহ ফাইলগুলির একটি তালিকাতে আইট্রেট করুন


201

আমি ফাইলগুলির একটি তালিকাতে পুনরাবৃত্তি করতে চাই। এই তালিকাটি একটি findআদেশের ফলাফল , তাই আমি এনেছি:

getlist() {
  for f in $(find . -iname "foo*")
  do
    echo "File found: $f"
    # do something useful
  done
}

কোনও ফাইলের নামে ফাঁকা স্থান ব্যতীত এটি ঠিক আছে:

$ ls
foo_bar_baz.txt
foo bar baz.txt

$ getlist
File found: foo_bar_baz.txt
File found: foo
File found: bar
File found: baz.txt

স্পেসে বিভাজন এড়াতে আমি কী করতে পারি?


উত্তর:


253

আপনি শব্দ-ভিত্তিক পুনরাবৃত্তিটি একটি লাইন-ভিত্তিক সাথে প্রতিস্থাপন করতে পারেন:

find . -iname "foo*" | while read f
do
    # ... loop body
done

31
এটি অত্যন্ত পরিষ্কার। এবং আমাকে লুপের সাথে একযোগে আইএফএসের পরিবর্তনের চেয়ে সুন্দর বোধ করে
ডেরিক

15
এটি এমন একক ফাইল পাথকে বিভক্ত করবে যেখানে একটি contains n রয়েছে। ঠিক আছে, touch "$(printf "foo\nbar")"
এগুলির

4
ইনপুটটির কোনও ব্যাখ্যা (ব্যাকস্ল্যাশগুলি, শীর্ষস্থানীয় এবং পূর্ববর্তী হোয়াইটস্পেস) প্রতিরোধ করতে, IFS= while read -r fপরিবর্তে ব্যবহার করুন।
এমকেলেট 0

2
এই উত্তরটি আরও কিছু সুরক্ষিত findএবং কিছুক্ষণের লুপটি দেখায় ।
moi

5
সুস্পষ্ট ইশারা মত মনে হয়, কিন্তু প্রায় সব সহজ ক্ষেত্রে, -execএকটি সুনির্দিষ্ট লুপ চেয়ে ক্লিনার হতে যাচ্ছে: find . -iname "foo*" -exec echo "File found: {}" \;। প্লাস, অনেক ক্ষেত্রে আপনি যে গত প্রতিস্থাপন করতে পারেন \;সঙ্গে +আর মাত্র একটি কমান্ড ফাইল প্রচুর করা।
nnot101

152

এটি সম্পাদন করার জন্য বেশ কয়েকটি কার্যক্ষম উপায় রয়েছে।

আপনি যদি আপনার মূল সংস্করণটির সাথে ঘনিষ্ঠভাবে থাকতে চান তবে এটি এইভাবে করা যেতে পারে:

getlist() {
        IFS=$'\n'
        for file in $(find . -iname 'foo*') ; do
                printf 'File found: %s\n' "$file"
        done
}

এটি এখনও ব্যর্থ হবে যদি ফাইলের নামগুলিতে আক্ষরিক নতুন লাইন থাকে তবে স্থানগুলি এটি ভাঙবে না।

তবে আইএফএসের সাথে জগাখিচির প্রয়োজন নেই। এটি করার জন্য আমার পছন্দের উপায়টি এখানে:

getlist() {
    while IFS= read -d $'\0' -r file ; do
            printf 'File found: %s\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

আপনি যদি < <(command)সিনট্যাক্সটি অপরিচিত দেখতে পান তবে আপনার প্রক্রিয়া প্রতিস্থাপন সম্পর্কে পড়া উচিত । এই ওভারের সুবিধা for file in $(find ...)হ'ল স্পেস, নিউলাইন এবং অন্যান্য অক্ষর সহ ফাইলগুলি সঠিকভাবে পরিচালনা করা হয়। এই কাজ করে কারণ findসঙ্গে -print0একটি ব্যবহার করা হবে null(ওরফে \0সম্পর্কে newline অসদৃশ প্রতিটি ফাইল নামের জন্য টারমিনেটর হিসাবে) এবং,, নাল একটি ফাইল নামে একটি আইনি চরিত্র নয়।

প্রায় সমতুল্য সংস্করণে এটির সুবিধা

getlist() {
        find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
                printf 'File found: %s\n' "$file"
        done
}

লুপটি সংরক্ষণের সময় এটির শরীরে কোনও পরিবর্তনশীল কাজ রয়েছে। এটি হ'ল যদি আপনি whileউপরের দিকে পাইপ করেন তবে এর শরীরটি whileএকটি সাবশেলে রয়েছে যা আপনি যা চান তা নাও হতে পারে।

প্রক্রিয়া প্রতিস্থাপন সংস্করণ ওভারের সুবিধাটি find ... -print0 | xargs -0ন্যূনতম: xargsসংস্করণটি ঠিক আছে যদি আপনার প্রয়োজন হয় তবে একটি লাইন মুদ্রণ করুন বা ফাইলে একটি একক ক্রিয়াকলাপ করা প্রয়োজন, তবে আপনাকে যদি একাধিক পদক্ষেপের প্রয়োজন হয় তবে লুপ সংস্করণটি সহজ।

সম্পাদনা : এখানে একটি দুর্দান্ত পরীক্ষার স্ক্রিপ্ট রয়েছে যাতে আপনি এই সমস্যাটি সমাধান করার ক্ষেত্রে বিভিন্ন চেষ্টার মধ্যে পার্থক্য সম্পর্কে ধারণা পেতে পারেন

#!/usr/bin/env bash

dir=/tmp/getlist.test/
mkdir -p "$dir"
cd "$dir"

touch       'file not starting foo' foo foobar barfoo 'foo with spaces'\
    'foo with'$'\n'newline 'foo with trailing whitespace      '

# while with process substitution, null terminated, empty IFS
getlist0() {
    while IFS= read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

# while with process substitution, null terminated, default IFS
getlist1() {
    while read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done < <(find . -iname 'foo*' -print0)
}

# pipe to while, newline terminated
getlist2() {
    find . -iname 'foo*' | while read -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# pipe to while, null terminated
getlist3() {
    find . -iname 'foo*' -print0 | while read -d $'\0' -r file ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# for loop over subshell results, newline terminated, default IFS
getlist4() {
    for file in "$(find . -iname 'foo*')" ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}

# for loop over subshell results, newline terminated, newline IFS
getlist5() {
    IFS=$'\n'
    for file in $(find . -iname 'foo*') ; do
            printf 'File found: '"'%s'"'\n' "$file"
    done
}


# see how they run
for n in {0..5} ; do
    printf '\n\ngetlist%d:\n' $n
    eval getlist$n
done

rm -rf "$dir"

1
আপনার উত্তর গৃহীত হয়েছে: সর্বাধিক সম্পূর্ণ এবং আকর্ষণীয় - আমি $IFSএবং < <(cmd)সিনট্যাক্স সম্পর্কে জানতাম না । তবুও একটা জিনিস আমার কাছে অস্পষ্ট, কেন $ভিতরে $'\0'? অনেক ধন্যবাদ.
গ্রেগসেথ

2
+1, তবে while IFS= readহোয়াইটস্পেসের সাথে শুরু হওয়া বা শেষ হওয়া ফাইলগুলি পরিচালনা করতে আপনার ... ... যুক্ত করা উচিত ।
গর্ডন ডেভিসন

1
প্রক্রিয়া প্রতিস্থাপন সমাধানের একটি সতর্কতা রয়েছে। লুপের ভিতরে যদি আপনার কোনও প্রম্পট থাকে (বা অন্য কোনও উপায়ে এসটিডিন থেকে পড়ছেন), আপনি যে লুপটিতে খাওয়াবেন তা দিয়ে ইনপুট পূরণ করা হবে। (সম্ভবত এটি উত্তরে যুক্ত করা উচিত?)
এবং

2
@ ওভসমতিড: এই প্রশ্নটি ট্যাগ করা হয়েছে bashতাই ব্যাশ-নির্দিষ্ট বৈশিষ্ট্যগুলি ব্যবহার করে আমি নিরাপদ বোধ করেছি। প্রক্রিয়া প্রতিস্থাপনটি অন্যান্য শেলগুলির কাছে পোর্টেবল নয় (sh নিজেই কখনও এ জাতীয় কোনও গুরুত্বপূর্ণ আপডেট পাওয়ার সম্ভাবনা থাকে না)।
sorpigal

2
মিশ্রন IFS=$'\n'সঙ্গে forপ্রতিরোধ লাইন অভ্যন্তরীণ শব্দ-বিভাজন, কিন্তু এখনও, globbing করার ফলে লাইন বিষয় করে তোলে তাই এই পদ্ধতির সম্পূর্ণরূপে শক্তসমর্থ নয় (যদি না আপনি প্রথমে globbing বন্ধ করুন)। যদিও read -d $'\0'কাজ, এটা সামান্য এটা দাবি করছে যে আপনি ব্যবহার করতে পারেন বিভ্রান্তিকর হয় $'\0'NULs তৈরি করার জন্য - তুমি পারবে না: একটি \0একটি ইন ANSI সি-উদ্ধৃত পংক্তি কার্যকরভাবে বন্ধ , স্ট্রিং, যাতে -d $'\0'কার্যকরভাবে হিসাবে একই -d ''
mklement0

29

খুব সহজ সমাধানও রয়েছে: ব্যাশ গ্লোব্বিংয়ের উপর নির্ভর করুন

$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid   file 3"
$ ls
stupid   file 3  stupid file1     stupid file2
$ for file in *; do echo "file: '${file}'"; done
file: 'stupid   file 3'
file: 'stupid file1'
file: 'stupid file2'

দ্রষ্টব্য যে আমি নিশ্চিত না যে এই আচরণটি পূর্বনির্ধারিত একটি তবে আমি আমার শপটে কোনও বিশেষ সেটিংস দেখতে পাচ্ছি না তাই আমি গিয়ে বলব যে এটি "নিরাপদ" হওয়া উচিত (ওক্স এবং উবুন্টুতে পরীক্ষা করা)।


13
find . -iname "foo*" -print0 | xargs -L1 -0 echo "File found:"

6
পার্শ্ব নোট হিসাবে, এটি কেবল তখনই কাজ করবে যদি আপনি কোনও কমান্ড কার্যকর করতে চান। একটি শেল বিল্টিন এইভাবে কাজ করবে না।
অ্যালেক্স


6

যেহেতু আপনি অন্য কোনও ধরণের ফিল্টারিং করছেন না find, আপনি নিম্নলিখিতটি bash4.0 হিসাবে ব্যবহার করতে পারেন :

shopt -s globstar
getlist() {
    for f in **/foo*
    do
        echo "File found: $f"
        # do something useful
    done
}

**/শূন্য বা তার বেশি ডিরেক্টরি ম্যাচ হবে, তাই পূর্ণ প্যাটার্ন ম্যাচ হবে foo*বর্তমান ডিরেক্টরী বা কোনো সাবডিরেক্টরি হবে।


3

আমি লুপগুলি এবং অ্যারে পুনরাবৃত্তির জন্য সত্যিই পছন্দ করি, তাই আমি অনুভব করি যে আমি এই উত্তরটি মিশ্রণটিতে যুক্ত করব ...

আমি মার্চেল্লিংয়ের বোকা ফাইলের উদাহরণটিও পছন্দ করেছি। :)

$ mkdir test
$ cd test
$ touch "stupid file1"
$ touch "stupid file2"
$ touch "stupid   file 3"

পরীক্ষা ডিরেক্টরি ভিতরে:

readarray -t arr <<< "`ls -A1`"

এটি প্রতিটি ফাইল তালিকা লাইনটিকে arrযেকোনো পেছনের নতুন লাইনের সাথে নাম দেওয়া বাশ অ্যারেতে যুক্ত করে ।

ধরা যাক আমরা এই ফাইলগুলিকে আরও ভাল নাম দিতে চাই ...

for i in ${!arr[@]}
do 
    newname=`echo "${arr[$i]}" | sed 's/stupid/smarter/; s/  */_/g'`; 
    mv "${arr[$i]}" "$newname"
done

$ {! অ্যারে [@] 0 0 1 2 তে প্রসারিত হয় সুতরাং "$ {আরার [$ i]}" অ্যারের i উপাদান। স্থানগুলি সংরক্ষণের জন্য ভেরিয়েবলের চারপাশে উদ্ধৃতিগুলি গুরুত্বপূর্ণ।

ফলাফলটি তিনটি নামকরণ করা ফাইল:

$ ls -1
smarter_file1
smarter_file2
smarter_file_3

2

findএকটি -execযুক্তি রয়েছে যা ফলাফলগুলি সন্ধান করে এবং একটি স্বেচ্ছাসেবক আদেশ কার্যকর করে। উদাহরণ স্বরূপ:

find . -iname "foo*" -exec echo "File found: {}" \;

এখানে {}পাওয়া ফাইলগুলি উপস্থাপন করে এবং এটিকে মোড়ানো ""ফলস্বরূপ শেল কমান্ডকে ফাইলের খালি জায়গাগুলি মোকাবেলা করার অনুমতি দেয়।

অনেক ক্ষেত্রে আপনি শেষটিকে \;(যা একটি নতুন কমান্ড শুরু করে) একটি দিয়ে প্রতিস্থাপন করতে পারেন \+, যা এক কমান্ডে একাধিক ফাইল স্থাপন করবে (অগত্যা তাদের সমস্তগুলি একবারে নয়, man findআরও বিশদ জানতে দেখুন)।


0

কিছু ক্ষেত্রে, আপনার যদি কেবলমাত্র ফাইলগুলির একটি তালিকা অনুলিপি করতে বা সরাতে হয় তবে আপনি সেই তালিকাটি বিশ্রী হয়েও পাইপ করতে পারেন। ক্ষেত্রের চারপাশের
গুরুত্বপূর্ণ (সংক্ষেপে আপনার ফাইলগুলি, একটি লাইন-তালিকা = একটি ফাইল)।\"" "\"$0

find . -iname "foo*" | awk '{print "mv \""$0"\" ./MyDir2" | "sh" }'

0

ঠিক আছে - স্ট্যাক ওভারফ্লোতে আমার প্রথম পোস্ট!

যদিও এর সাথে আমার সমস্যাগুলি বরাবরই সিএস-এর মধ্যে রয়েছে আমি যে সমাধানটি উপস্থাপন করব তা বশ না করে, আমি নিশ্চিত, উভয়েই কাজ করব। সমস্যাটি "এলএস" রিটার্নগুলির শেলের ব্যাখ্যার সাথে। আমরা *ওয়াইল্ডকার্ডের শেল প্রসারকে সহজভাবে ব্যবহার করে সমস্যা থেকে "এলএস" মুছে ফেলতে পারি - তবে বর্তমান (বা নির্দিষ্ট ফোল্ডারে) কোনও ফাইল না থাকলে এটি "কোনও মিলবে না" ত্রুটি দেয় - এটি ঘটার জন্য আমরা কেবল প্রসারিত করতে পারি এইভাবে ডট-ফাইল অন্তর্ভুক্ত করার জন্য সম্প্রসারণ: * .*- ফাইলগুলির পরে এটি সর্বদা ফলাফল দেয়। এবং .. সর্বদা উপস্থিত থাকবে। সুতরাং সিএসএসে আমরা এই নির্মাণটি ব্যবহার করতে পারি ...

foreach file (* .*)
   echo $file
end

যদি আপনি স্ট্যান্ডার্ড ডট-ফাইলগুলি ফিল্টার করতে চান তবে তা যথেষ্ট সহজ ...

foreach file (* .*)
   if ("$file" == .) continue
   if ("file" == ..) continue
   echo $file
end

এই থ্রেডে প্রথম পোস্টের কোডটি এইভাবে লেখা হবে: -

getlist() {
  for f in $(* .*)
  do
    echo "File found: $f"
    # do something useful
  done
}

আশাকরি এটা সাহায্য করবে!


0

কাজের আরেকটি সমাধান ...

লক্ষ্যটি ছিল:

  • ডিরেক্টরিতে পুনরাবৃত্তভাবে ফাইলের নামগুলি নির্বাচন / ফিল্টার করুন
  • প্রতিটি নাম হ্যান্ডেল করুন (পথে যা কিছু স্থান ...)
#!/bin/bash  -e
## @Trick in order handle File with space in their path...
OLD_IFS=${IFS}
IFS=$'\n'
files=($(find ${INPUT_DIR} -type f -name "*.md"))
for filename in ${files[*]}
do
      # do your stuff
      #  ....
done
IFS=${OLD_IFS}


গঠনমূলক মন্তব্য করার জন্য Thx, কিন্তু: 1- এটি একটি আসল সমস্যা, 2- শেলটি সেই সময়ে বিকশিত হতে পারে ... আমি প্রত্যেকেই ধরে নিয়েছি; 3- উপরের কোনও উত্তরই সমস্যাটি পরিবর্তন না করে বা গবেষণামূলক প্রবন্ধ ছাড়াই পিবি-র একটি DIRECT রেজোলিউশন সন্তুষ্ট করতে পারে না
ভিনস বি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.