পাইপ থেকে শেল ভেরিয়েবলে মানগুলি পড়ুন


205

আমি স্টিডিন থেকে ডেটা প্রক্রিয়া করার জন্য বাশ পাওয়ার চেষ্টা করছি যা পাইপ হয়ে গেছে, তবে ভাগ্য নেই। আমি যা বলতে চাইছি তা নিচের কোনও কাজ নয়:

echo "hello world" | test=($(< /dev/stdin)); echo test=$test
test=

echo "hello world" | read test; echo test=$test
test=

echo "hello world" | test=`cat`; echo test=$test
test=

যেখানে আমি আউটপুট হতে চাই test=hello world। আমি "" এর চারপাশে উদ্ধৃতি দেওয়ার চেষ্টা করেছি "$test"যা কার্যকর হয় না।


1
আপনার উদাহরণ .. প্রতিধ্বনি "হ্যালো ওয়ার্ল্ড" | পরীক্ষা পড়া; প্রতিধ্বনি পরীক্ষা = $ পরীক্ষা আমার পক্ষে ভাল কাজ করেছে .. ফলাফল: পরীক্ষা = হ্যালো ওয়ার্ল্ড; কোন পরিবেশটি এই অধীনে চলছে? আমি বাশ 4.2 ব্যবহার করছি ..
alex.pilon

আপনি কি একক পঠনে একাধিক লাইন চান? আপনার উদাহরণটি কেবল একটি লাইন দেখায় তবে সমস্যার বিবরণ অস্পষ্ট।
চার্লস ডাফি

2
@ অ্যালেক্স.পিলন, আমি বাশ সংস্করণ ৪.২.২৫ চালাচ্ছি, এবং তার উদাহরণটি আমার পক্ষেও কার্যকর হয় না। এটি কোনও বাশ রানটাইম বিকল্প বা পরিবেশের পরিবর্তনশীলের বিষয় হতে পারে? আমি উদাহরণটি শি এর সাথেও কাজ করি না, তাই বাশ কি শ এর সাথে সামঞ্জস্য হওয়ার চেষ্টা করতে পারে?
Hibou57

2
@ হিবিউ ৫7 - আমি এটি আবার 4.3.25 বাশে চেষ্টা করেছি এবং এটি আর কাজ করে না। আমার স্মৃতিশক্তিটি এটি নিয়ে অস্পষ্ট এবং আমি নিশ্চিত না যে এটি কাজ করতে আমি কী করেছি।
alex.pilon

2
@ হিবিউ ৫ @ @ অ্যালেক্স.পিলন একটি পাইপে সর্বশেষ সেন্টিমিডে bash4> = 4.2 এর সাথে shopt -s lastpipe- tldp.org/LDP/abs/html/bashver4.html#LASTPIPEOPT
imz - ইভান জ্যাকারিয়াশেভ

উত্তর:


162

ব্যবহার

IFS= read var << EOF
$(foo)
EOF

আপনি এই জাতীয় পাইপ থেকে গ্রহণ করার কৌশল করতে পারেনread :

echo "hello world" | { read test; echo test=$test; }

বা এমনকি এখানে একটি ফাংশন লিখুন:

read_from_pipe() { read "$@" <&0; }

তবে এর কোনও মানে নেই - আপনার ভেরিয়েবল অ্যাসাইনমেন্টগুলি নাও থাকতে পারে! একটি পাইপলাইন সাবস্কেল তৈরি করতে পারে, যেখানে পরিবেশটি মূল্য হিসাবে উত্তরাধিকার সূত্রে প্রাপ্ত নয়, রেফারেন্সের মাধ্যমে নয়। এই কারণেই readকোনও পাইপ থেকে ইনপুট নিয়ে মাথা ঘামায় না - এটি অপরিজ্ঞাত।

এফওয়াইআই, http://www.etalabs.net/sh_tricks.html বোর্ন শেলগুলির অদ্ভুততা এবং অসঙ্গতিগুলির বিরুদ্ধে লড়াই করার জন্য প্রয়োজনীয় ক্রাফ্টের একটি নিফটির সংগ্রহ sh


পরিবর্তে আপনি এই কাজটি শেষ করে শেষ করতে পারেন: `পরীক্ষা =` `প্রতিধ্বনি" হ্যালো ওয়ার্ল্ড "| {পরীক্ষা পড়া; প্রতিধ্বনি $ পরীক্ষা; `` ``
কমপোলিও

2
আসুন এটি আবার চেষ্টা করুন (স্পষ্টতই এই মার্কআপে ব্যাকটিক্স থেকে বাঁচা মজাদার):test=`echo "hello world" | { read test; echo $test; }`
কমফোলিও

আমি জিজ্ঞাসা করতে পারি যে আপনি কেন এই দুটি কমান্ডকে গ্রুপিংয়ের {}পরিবর্তে ব্যবহার করেছেন ()?
জর্জেন পল

4
কৌশলটি readপাইপটি থেকে ইনপুট নেওয়ার ক্ষেত্রে নয়, একই শেলের মধ্যে পরিবর্তনশীল ব্যবহার করে যা কার্যকর করে read
চিপনার

1
bash permission deniedএই সমাধানটি ব্যবহার করার চেষ্টা করার সময় পেয়েছি । আমার ক্ষেত্রে পুরোপুরি ভিন্ন, কিন্তু আমি যে কোন জায়গায় (একটি পৃথক উদাহরণস্বরূপ কিন্তু অনুরূপ ব্যবহার) এটা জন্য একটি উত্তর, কি আমার জন্য কাজ ছিল খুঁজে পাওয়া যায়নি: pip install -U echo $(ls -t *.py | head -1)। সেক্ষেত্রে কারও কারওর মতো একই সমস্যা রয়েছে এবং আমার মতো এই উত্তরটি নিয়ে হোঁচট খায়।
আইভান_বিলান

111

আপনি যদি প্রচুর ডেটাতে পড়তে চান এবং প্রতিটি লাইনে আলাদাভাবে কাজ করতে চান তবে আপনি এই জাতীয় কিছু ব্যবহার করতে পারেন:

cat myFile | while read x ; do echo $x ; done

আপনি যদি লাইনগুলিকে একাধিক শব্দের মধ্যে বিভক্ত করতে চান তবে আপনি এক্স এর জায়গায় একাধিক ভেরিয়েবল ব্যবহার করতে পারেন:

cat myFile | while read x y ; do echo $y $x ; done

অন্যথায়:

while read x y ; do echo $y $x ; done < myFile

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

perl -ane 'print "$F[0]\n"' < myFile

পার্লের সাথে মোটামুটি খাড়া শেখার বক্ররেখা রয়েছে (বা আমি এর মধ্যে যে কোনও একটি ভাষা অনুমান করি) তবে আপনি যদি স্ক্রিপ্টগুলির সহজতম কিছু ছাড়া কিছু করতে চান তবে আপনি দীর্ঘমেয়াদে এটি অনেক সহজ খুঁজে পাবেন। আমি পার্ল কুকবুক এবং অবশ্যই ল্যারি ওয়াল এট-এর পার্ল প্রোগ্রামিং ল্যাঙ্গুয়েজের প্রস্তাব দিই।


8
"বিকল্পভাবে" সঠিক উপায়। কোনও ইউইউসি এবং কোনও সাবশেল নেই। দেখুন BashFAQ / 024
পরবর্তী বিজ্ঞপ্তি না হওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

43

readকোন পাইপ থেকে পড়বে না (বা সম্ভবত ফলাফলটি হারিয়ে গেছে কারণ পাইপটি একটি সাবশেল তৈরি করে)। আপনি তবে বাশে এখানে একটি স্ট্রিং ব্যবহার করতে পারেন:

$ read a b c <<< $(echo 1 2 3)
$ echo $a $b $c
1 2 3

তবে তথ্যের জন্য @ চিপনার উত্তর দেখুন lastpipe


সুন্দর এবং সহজ ওয়ান-লাইনার, সহজেই বোঝা যায়। এই উত্তরের আরও উন্নতি প্রয়োজন।
ডেভিড পার্কস

42

এটি অন্য একটি বিকল্প

$ read test < <(echo hello world)

$ echo $test
hello world

24
উল্লেখযোগ্য সুবিধা হলো <(..)বেশি হয়েছে $(..)যে <(..)আয় কলারের কাছে প্রতিটি লাইনে যত কমান্ড এটি executes এটি উপলব্ধ তোলে শীঘ্রই। $(..)যাইহোক, কমান্ড অপেক্ষা করে অপেক্ষা করে যে এটি কলারের জন্য কোনও আউটপুট উপলব্ধ করার আগে তার সমস্ত আউটপুট সম্পূর্ণ এবং জেনারেট করে।
ডেরেক মাহর

37

আমি বাশ-এ কোনও বিশেষজ্ঞ নই, তবে আমি কেন অবাক হব তা ভাবছি না:

stdin=$(cat)

echo "$stdin"

এটি আমার পক্ষে কাজ করে এমন এক-লাইনের প্রমাণ:

$ fortune | eval 'stdin=$(cat); echo "$stdin"'

4
এটি সম্ভবত কারণ "রিড" বাশ কমান্ড, এবং বিড়াল একটি পৃথক বাইনারি যা একটি সাবপ্রসেসে চালু করা হবে, সুতরাং এটি কম দক্ষ।
ডিজে_সেগফল্ট

10
কখনও কখনও সরলতা এবং স্পষ্টতা ট্রাম্প দক্ষতা :)
রন্ডো

5
অবশ্যই সবচেয়ে সোজা-এগিয়ে উত্তর
drwatsoncode

এটির সাথে আমি যে সমস্যাটি চালিয়েছি তা হ'ল যদি কোনও পাইপ ব্যবহার না করা হয় তবে স্ক্রিপ্টটি স্তব্ধ হয়ে যায়।
ডেল অ্যান্ডারসন

@ ডেলাআ আচ্ছা অবশ্যই। যে কোনও প্রোগ্রাম যা স্ট্যান্ডার্ড ইনপুট থেকে পড়ার চেষ্টা করে তা যদি কিছু না খাওয়ানো হয় তবে "স্তব্ধ" হয়ে যাবে।
djanowski

27

bash৪.২ lastpipeঅপশনটি প্রবর্তন করে , যা আপনার কোডটি সাবশেলের পরিবর্তে বর্তমান শেলের পাইপলাইনে শেষ কমান্ডটি সম্পাদন করে লিখিত হিসাবে আপনার কোডকে কাজ করার অনুমতি দেয়।

shopt -s lastpipe
echo "hello world" | read test; echo test=$test

2
আহ! খুব ভাল, এই। যদি ইন্টারেক্টিভ শেলটিতে পরীক্ষা করা হয় তবে এটিও: "সেট + এম" (এস। এস স্ক্রিপ্টে প্রয়োজন হয় না)
এক্সএক্সএল

14

একটি শেল কমান্ড থেকে ব্যাশ ভেরিয়েবলের মধ্যে অন্তর্ভুক্ত পাইপের জন্য বাক্য গঠন

var=$(command)

অথবা

var=`command`

আপনার উদাহরণগুলিতে, আপনি কোনও অ্যাসাইনমেন্ট স্টেটমেন্টে ডেটা পাইপ করছেন, যা কোনও ইনপুট আশা করে না।


4
কারণ $ () সহজেই বাসা বাঁধতে পারে। জাভা_ডির = $ (ডায়ারনেম $ (রিডলিঙ্ক-ফ $ (যা জাভা))) ভাবেন, এবং এটি দিয়ে try দিয়ে চেষ্টা করুন ` আপনার তিনবার পালাতে হবে!
আলবফান

8

প্রথম প্রচেষ্টা বেশ কাছাকাছি ছিল। এই প্রকরণটির কাজ করা উচিত:

echo "hello world" | { test=$(< /dev/stdin); echo "test=$test"; };

এবং আউটপুটটি হ'ল:

পরীক্ষা = হ্যালো ওয়ার্ল্ড

অ্যাসাইনমেন্টটি পরীক্ষা করার জন্য প্রতিধ্বনি এবং প্রতিধ্বনিটি বন্ধ করার জন্য আপনার পাইপের পরে ধনুর্বন্ধনী প্রয়োজন।

ধনুর্বন্ধনী ব্যতীত পরীক্ষা করার জন্য অ্যাসাইনমেন্ট (পাইপের পরে) একটি শেলের মধ্যে থাকে এবং প্রতিধ্বনি "টেস্ট = $ পরীক্ষা" একটি পৃথক শেলের মধ্যে থাকে যা সেই অ্যাসাইনমেন্ট সম্পর্কে জানে না। এজন্য আপনি আউটপুটে "পরীক্ষা = হ্যালো ওয়ার্ল্ড" এর পরিবর্তে "পরীক্ষা =" পেয়ে যাচ্ছেন।


8

আমার দৃষ্টিতে স্ট্যাডিন থেকে বাশের পাঠের সর্বোত্তম উপায়টি হ'ল নিম্নলিখিতটি, যা আপনাকে ইনপুটটি শেষ হওয়ার আগে লাইনে কাজ করতে দেয়:

while read LINE; do
    echo $LINE
done < /dev/stdin

1
আমি এটি সন্ধানের আগে প্রায় পাগল হয়ে গেলাম। ধন্যবাদ ভাগ করে নেওয়ার জন্য অনেক!
স্যামি ডিন্ডেন

6

কারণ আমি এর জন্য পড়েছি, আমি একটি নোট ফেলে দিতে চাই। আমি এই থ্রেডটি পেয়েছি কারণ পসিক্স সামঞ্জস্যপূর্ণ হওয়ার জন্য আমাকে একটি পুরানো শ স্ক্রিপ্টটি আবার লিখতে হবে। এর মূল অর্থ POSIX দ্বারা প্রবর্তিত পাইপ / সাবশেল সমস্যাটিকে এ জাতীয় কোড লিখে পুনরায় লেখার দ্বারা বিরত করা:

some_command | read a b c

মধ্যে:

read a b c << EOF
$(some_command)
EOF

এবং কোড এর মত:

some_command |
while read a b c; do
    # something
done

মধ্যে:

while read a b c; do
    # something
done << EOF
$(some_command)
EOF

তবে পরেরটি খালি ইনপুটতে একই আচরণ করে না। পুরানো স্বরলিপি সহ যখন লুপটি খালি ইনপুটটিতে প্রবেশ করা হয় না, তবে পসিক্স স্বরলিপিতে এটি হয়! আমি মনে করি এটি EOF এর আগে নতুন লাইনের কারণে, যা বাদ দেওয়া যায় না। পুসি কোডটি পুরানো স্বরলিপিটির মতো আরও আচরণ করে যা দেখে মনে হয়:

while read a b c; do
    case $a in ("") break; esac
    # something
done << EOF
$(some_command)
EOF

বেশিরভাগ ক্ষেত্রে এটি যথেষ্ট ভাল হওয়া উচিত। তবে দুর্ভাগ্যক্রমে এটি এখনও পুরানো স্বরলিপিটির মতো হুবহু আচরণ করে না যদি কিছু_কম্যান্ড একটি ফাঁকা লাইন প্রিন্ট করে। পুরানো স্বরলিপিটিতে দেহটি কার্যকর করা হয় এবং পসিক্স স্বরলিপিতে আমরা দেহের সামনে ভাঙি।

এটির সমাধানের জন্য একটি পদ্ধতির মতো দেখতে এটি হতে পারে:

while read a b c; do
    case $a in ("something_guaranteed_not_to_be_printed_by_some_command") break; esac
    # something
done << EOF
$(some_command)
echo "something_guaranteed_not_to_be_printed_by_some_command"
EOF

5

একটি স্মার্ট স্ক্রিপ্ট যা উভয়ই পিআইপিই এবং কমান্ড লাইন আর্গুমেন্ট থেকে ডেটা পড়তে পারে:

#!/bin/bash
if [[ -p /proc/self/fd/0 ]]
    then
    PIPE=$(cat -)
    echo "PIPE=$PIPE"
fi
echo "ARGS=$@"

আউটপুট:

$ bash test arg1 arg2
ARGS=arg1 arg2

$ echo pipe_data1 | bash test arg1 arg2
PIPE=pipe_data1
ARGS=arg1 arg2

ব্যাখ্যা: যখন কোনও স্ক্রিপ্ট পাইপের মাধ্যমে কোনও ডেটা গ্রহণ করে, তারপরে স্টিডিন / প্রক / স্ব / এফডি / 0 পাইপের সাথে একটি সিমিলিংক হবে।

/proc/self/fd/0 -> pipe:[155938]

যদি তা না হয় তবে এটি বর্তমান টার্মিনালের দিকে নির্দেশ করবে:

/proc/self/fd/0 -> /dev/pts/5

বাশ [[ -pবিকল্প এটি পরীক্ষা করতে পারে এটি একটি পাইপ কিনা or

cat -থেকে পড়া stdin

আমরা cat -যদি সেখানে নেই তখন stdinএটি ব্যবহার করি , এটি চিরকাল অপেক্ষা করবে, সে কারণেই আমরা এটিকে ifশর্তের মধ্যে রাখি ।


আপনি /dev/stdinযা ব্যবহার করতে পারেন তার লিঙ্কটি/proc/self/fd/0
এলি জি

3

কোনও অ্যাসাইনমেন্ট জড়িত কোনও অভিব্যক্তিগুলিতে পাইপ দেওয়া সেভাবে আচরণ করে না।

পরিবর্তে, চেষ্টা করুন:

test=$(echo "hello world"); echo test=$test

2

নিম্নলিখিত কোড:

echo "hello world" | ( test=($(< /dev/stdin)); echo test=$test )

খুব কাজ করবে, তবে এটি পাইপের পরে আরও একটি নতুন সাব-শেল খুলবে, যেখানে

echo "hello world" | { test=($(< /dev/stdin)); echo test=$test; }

না।


চেপনার পদ্ধতি ব্যবহার করার জন্য আমাকে জব নিয়ন্ত্রণ অক্ষম করতে হয়েছিল (আমি এই কমান্ডটি টার্মিনাল থেকে চালাচ্ছিলাম):

set +m;shopt -s lastpipe
echo "hello world" | read test; echo test=$test
echo "hello world" | test="$(</dev/stdin)"; echo test=$test

বাশ ম্যানুয়াল বলেছেন :

lastpipe

যদি সেট করা থাকে এবং জব নিয়ন্ত্রণ সক্রিয় না হয়, শেলটি বর্তমান শেল পরিবেশে পটভূমিতে কার্যকর করা হয়নি এমন পাইপলাইনের শেষ কমান্ডটি চালায়।

দ্রষ্টব্য: জব নিয়ন্ত্রণটি অ-ইন্টারেক্টিভ শেলটিতে ডিফল্টরূপে বন্ধ থাকে এবং সুতরাং আপনার set +mকোনও স্ক্রিপ্টের অভ্যন্তরের প্রয়োজন হবে না ।


1

আমি মনে করি আপনি শেল স্ক্রিপ্ট লেখার চেষ্টা করছেন যা স্টিডিনের কাছ থেকে ইনপুট নিতে পারে। তবে আপনি যখন এটিকে ইনলাইন করার চেষ্টা করছেন, আপনি সেই পরীক্ষাটি = পরিবর্তনশীল তৈরির চেষ্টা হারিয়ে ফেলেন। আমি মনে করি এটি ইনলাইন করতে খুব বেশি অর্থবোধ করে না এবং তাই এটি আপনার প্রত্যাশার মতো কার্যকর হয় না।

আমি হ্রাস করার চেষ্টা করছিলাম

$( ... | head -n $X | tail -n 1 )

বিভিন্ন ইনপুট থেকে একটি নির্দিষ্ট লাইন পেতে। তাই আমি টাইপ করতে পারি ...

cat program_file.c | line 34

সুতরাং আমার একটি ছোট শেল প্রোগ্রাম দরকার যা স্টিডিন থেকে পড়তে সক্ষম। তোমার পছন্দ মতই করো.

22:14 ~ $ cat ~/bin/line 
#!/bin/sh

if [ $# -ne 1 ]; then echo enter a line number to display; exit; fi
cat | head -n $1 | tail -n 1
22:16 ~ $ 

এই নাও.


-1

এটি সম্পর্কে:

echo "hello world" | echo test=$(cat)

নীচে আমার উত্তর থেকে মূলত একটি দ্বি।
djanowski

হতে পারে, আমার যুক্তিটি হবে যে খানিকটা পরিষ্কার এবং মূল প্রশ্নে পোস্ট করা কোডের কাছাকাছি।
বিঙ্গুলগুলি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.