স্ট্রিং ভেরিয়েবলের লাইনের সংখ্যা গণনা করার জন্য কীভাবে পসিক্স-ল্য?


10

আমি জানি আমি এটি ব্যাশে করতে পারি:

wc -l <<< "${string_variable}"

মূলত, আমি যা পেয়েছি তার প্রত্যেকটিতে <<<ব্যাশ অপারেটর জড়িত ।

তবে পসিক্স শেলের মধ্যে <<<, অপরিবর্তিত রয়েছে এবং আমি কয়েক ঘন্টার জন্য বিকল্প পদ্ধতির সন্ধান করতে অক্ষম। আমি এটির একটি সহজ সমাধান আছে তা নিশ্চিত, কিন্তু দুর্ভাগ্যক্রমে, আমি এখনও এটি খুঁজে পাইনি।

উত্তর:


11

সহজ উত্তরটি হ'ল wc -l <<< "${string_variable}"একটি ksh / bash / zsh শর্টকাট printf "%s\n" "${string_variable}" | wc -l

উপায় <<<এবং পাইপের কাজের মধ্যে আসলে পার্থক্য রয়েছে : <<<একটি অস্থায়ী ফাইল তৈরি করে যা কমান্ডের ইনপুট হিসাবে পাস হয়, যেখানে |একটি পাইপ তৈরি করে। বাশ এবং পিডিএক্স / এমক্ষে (তবে ksh93 বা zsh এ নয়) পাইপের ডানদিকে কমান্ডটি একটি সাবশেলে চলে। তবে এই পার্থক্যগুলি এই বিশেষ ক্ষেত্রে গুরুত্বপূর্ণ নয়।

দ্রষ্টব্য যে গণনা রেখার ক্ষেত্রে, এটি ধরে নেওয়া যায় যে ভেরিয়েবলটি খালি নয় এবং একটি নতুন লাইনের সাথে শেষ হয় না। ভেরিয়েবল কমান্ড প্রতিস্থাপনের ফলাফল হিসাবে যখন একটি নতুন লাইন দিয়ে শেষ না হয়, সুতরাং বেশিরভাগ ক্ষেত্রে আপনি সঠিক ফলাফল পাবেন তবে খালি স্ট্রিংয়ের জন্য আপনি 1 পাবেন।

তার মাঝে দুই পার্থক্য আছে var=$(somecommand); wc -l <<<"$var"এবং somecommand | wc -l: একটি কমান্ড প্রতিকল্পন ব্যবহার করে এবং একটি অস্থায়ী পরিবর্তনশীল দূরে শেষে ফাঁকা লাইন, ভুলে কিনা আউটপুট শেষ লাইনটি বা না একটি newline সালে শেষ রেখাচিত্রমালা (এটা সবসময় কমান্ড একটি বৈধ nonempty টেক্সট ফাইল আউটপুট যদি না) , এবং আউটপুট খালি থাকলে একের পর এক অতিরিক্ত ওভার অ্যাকাউন্ট করে। আপনি যদি ফলাফল এবং গণনা লাইন উভয়ই সংরক্ষণ করতে চান, তবে আপনি কিছু পরিচিত পাঠ্য সংযোজন করে এবং শেষে তা সরিয়ে দিয়ে এটি করতে পারেন:

output=$(somecommand; echo .)
line_count=$(($(printf "%s\n" "$output" | wc -l) - 1))
printf "The exact output is:\n%s" "${output%.}"

1
@ ইনিয়ান কিপিং মূলটির wc -lসাথে ঠিক সমান: মানটিতে <<<$fooএকটি নতুন লাইন যুক্ত করে $foo(এমনকি $fooশূন্য থাকলেও )। আমি আমার উত্তরে ব্যাখ্যা করেছিলাম কেন এটি যা চেয়েছিল তা নাও হতে পারে তবে এটিই যা জিজ্ঞাসা করা হয়েছিল।
গিলস'স'- দুষ্ট হওয়া বন্ধ করুন '

2

মত বাহ্যিক ইউটিলিটি ব্যবহার করে, বিল্ট-ইন শেল সঙ্গে মানানসই নয় grepএবং awkPOSIX অনুবর্তী বিকল্প সহ

string_variable="one
two
three
four"

grepলাইনের শুরুতে ম্যাচ করার জন্য করছেন

printf '%s' "${string_variable}" | grep -c '^'
4

এবং সাথে awk

printf '%s' "${string_variable}" | awk 'BEGIN { count=0 } NF { count++ } END { print count }'

নোট করুন যে কয়েকটি GNU সরঞ্জাম, বিশেষত, GNU সরঞ্জামটির POSIX সংস্করণ চালনার বিকল্পটিকে grepসম্মান করে না POSIXLY_CORRECT=1। ইন grepশুধুমাত্র আচরণ পরিবর্তনশীল সেটিং দ্বারা প্রভাবিত কমান্ড লাইন পতাকার অর্ডার প্রক্রিয়াকরণের মধ্যে পার্থক্য থাকবে না। ডকুমেন্টেশন (জিএনইউ grepম্যানুয়াল) থেকে এটি মনে হয়

POSIXLY_CORRECT

যদি সেট করা থাকে, গ্রেপ POSIX এর মতো আচরণ করে; অন্যথায়, grepঅন্যান্য জিএনইউ প্রোগ্রামগুলির মতো আচরণ করে। পসিক্সের প্রয়োজন যে ফাইলগুলির নাম অনুসরণ করে এমন বিকল্পগুলি অবশ্যই ফাইলের নাম হিসাবে গণ্য হবে; ডিফল্টরূপে, এই জাতীয় অপশন অপারেন্ড তালিকার সামনের দিকে অনুমতি দেওয়া হয় এবং বিকল্প হিসাবে বিবেচিত হয়।

গ্রাপে POSIXLY_CORRECT কীভাবে ব্যবহার করবেন দেখুন ?


2
অবশ্যই wc -lএখানে এখনও কার্যকর?
মাইকেল হোমার

@ মিশেলহোমর: আমি যা পর্যবেক্ষণ করেছি তার থেকে wc -lযথাযথ নিউলাইন সীমানা প্রবাহের প্রয়োজন (সঠিকভাবে গণনা করার জন্য শেষের দিকে 'tra n\' থাকা)। printfprintf '%s' "${string_variable}" | wc -l<<<\n
এটির

1
printf '%s\n'আপনি এটি বের করার আগে এটি ছিল যা ছিল ...
মাইকেল হোমার

1

এখানে স্ট্রিং <<<প্রায় কাছাকাছি এখানে-দস্তাবেজের একটি এক-লাইন সংস্করণ <<। পূর্বেরটি কোনও মানসম্পন্ন বৈশিষ্ট্য নয়, তবে পরবর্তীটি। আপনি <<এই ক্ষেত্রে খুব ব্যবহার করতে পারেন । এগুলি সমতুল্য হওয়া উচিত:

wc -l <<< "$somevar"

wc -l << EOF
$somevar
EOF

যদিও লক্ষ্য করুন যে উভয়ই শেষের দিকে একটি অতিরিক্ত নতুন লাইন যুক্ত করে $somevar, যেমন এই মুদ্রণগুলি 6, যদিও ভেরিয়েবলের কেবল পাঁচটি লাইন থাকে:

s=$'foo\n\n\nbar\n\n'
wc -l <<< "$s"

এর সাথে printf, আপনি অতিরিক্ত নিউলাইন চান কিনা তা আপনি সিদ্ধান্ত নিতে পারেন:

printf "%s\n" "$s" | wc -l         # 6
printf "%s"   "$s" | wc -l         # 5

তবে তবে মনে রাখবেন যে wcকেবলমাত্র সম্পূর্ণ লাইন গণনা করা হয় (বা স্ট্রিংয়ে নতুনরেখার অক্ষরের সংখ্যা)। grep -c ^চূড়ান্ত লাইন খণ্ডটিও গণনা করা উচিত।

s='foo'
printf "%s" "$s" | wc -l           # 0 !

printf "%s" "$s" | grep -c ^       # 1

(অবশ্যই আপনি ${var%...}লুপের মধ্যে একবারে এটি মুছে ফেলার জন্য সম্প্রসারণটি ব্যবহার করে শেলটিতে সম্পূর্ণ লাইনগুলি গণনা করতে পারেন ...)


0

এই আশ্চর্যরকম ঘন ঘন ক্ষেত্রে যেখানে আপনাকে আসলে কিছুটা ফ্যাশনের পরিবর্তনের পরিবর্তে সমস্ত শূন্য ফাঁকা লাইনগুলি প্রক্রিয়া করা হয় (সেগুলি গণনা সহ), আপনি আইএফএসকে কেবল একটি নতুন লাইনে সেট করতে পারেন এবং তারপরে শেলের শব্দ বিভাজন প্রক্রিয়াটি ভাঙ্গতে ব্যবহার করতে পারেন খালি খালি লাইন পৃথক।

উদাহরণস্বরূপ, এখানে একটি সামান্য শেল ফাংশন যা সমস্ত সরবরাহিত আর্গুমেন্টের মধ্যে খালি খালি লাইনগুলি যোগ করে:

lines() (
IFS='
'
set -f #disable pathname expansion
set -- $*
echo $#
)

বন্ধনীগুলির পরিবর্তে প্যারেন্টিসগুলি এখানে ফাংশন বডিটির জন্য যৌগিক কমান্ড গঠনে ব্যবহৃত হয়। এটি ফাংশনটিকে একটি সাবশেলে কার্যকর করতে সক্ষম করে যাতে এটি বাইরের বিশ্বের আইএফএস ভেরিয়েবল এবং প্রতিটি কলের প্যাথনাম এক্সপেনশন সেটিংসকে দূষিত না করে।

আপনি যদি খালি খালি লাইনে পুনরাবৃত্তি করতে চান তবে আপনি এটি একইভাবে করতে পারেন:

IFS='
'
set -f
for line in $lines
do
    printf '[%s]\n' $line
done

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

উদাহরণস্বরূপ, আপনি যদি কোনও জটিল কমান্ড লাইন তৈরির জন্য ভেরিয়েবলগুলি ব্যবহার করে থাকেন তবে ভেরিয়েবলটি কেবল কোনও শূন্য নয় এমনটিতে সেট করা থাকলে ffmpegআপনি অন্তর্ভুক্ত করতে চাইতে পারেন । সাধারণত আপনার সাথে এই অর্জন করতে পারে কিন্তু যদি IFS সময় এই প্যারামিটারটি সম্প্রসারণ সম্পন্ন করা হয় তার স্বাভাবিক স্থান অক্ষর অন্তর্ভুক্ত নয়, স্থান মধ্যে এবং একটি শব্দ বিভাজক হিসেবে ব্যবহার করা হবে না এবং সব পাস হবে একটি একক আর্গুমেন্ট হিসাবে, যা এটি বুঝতে পারে না।-vf scale=$scalescale${scale:+-vf scale=$scale}-vfscale=ffmpeg-vf scale=$scale

এটি ঠিক করার জন্য, আপনাকে নিশ্চিত IFS কাজ করার আগে আরও স্বাভাবিকভাবে হয় করতে পারেন প্রয়োজন চাই ${scale}সম্প্রসারণ, বা দুই প্রসারণও করুন: ${scale:+-vf} ${scale:+scale=$scale}। শেলটি কমান্ড লাইনগুলির প্রাথমিক পার্সিংয়ের প্রক্রিয়াতে শেলটি যে শব্দটি বিভক্ত করে তার বিপরীতে command কমান্ড লাইনের প্রসেসিংয়ের প্রসারণের পর্যায়ে বিভক্ত হওয়ার বিপরীতে, আইএফএসের উপর নির্ভর করে না।

আপনি যদি এই ধরণের জিনিসটি করতে যাচ্ছেন তবে অন্য কিছু যা আপনার পক্ষে মূল্যবান হতে পারে তা কেবল একটি ট্যাব এবং কেবল একটি নতুন লাইন ধরে রাখতে দুটি বৈশ্বিক শেল ভেরিয়েবল তৈরি করবে:

t=' '
n='
'

এই ভাবে আপনি কেবলমাত্র অন্তর্ভুক্ত করতে পারেন $tএবং $nযেখানে আপনার ট্যাব এবং নিউলাইনগুলি প্রয়োজন সেখানে বিস্তৃত হোয়াইটস্পেসের সাহায্যে আপনার সমস্ত কোড লিটারের চেয়ে। আপনি যদি কোনও পসিক্স শেলের সাথে সম্পূর্ণরূপে উদ্ধৃত শ্বেতক্ষেত্র এড়াতে চান তবে এর জন্য অন্য কোনও ব্যবস্থা নেই, printfতবে আপনাকে সাহায্য করতে পারেন যদিও কমান্ড বিস্তারে নতুন লাইনের পথ অনুসরণের জন্য আপনাকে কিছুটা ফিডিং করতে হবে:

nt=$(printf '\n\t')
n=${nt%?}
t=${nt#?}

কখনও কখনও আইএফএস স্থাপন করে যেমন এটি প্রতি-কমান্ড পরিবেশের পরিবর্তনশীল কাজ করে vari উদাহরণস্বরূপ, এখানে এমন একটি লুপ রয়েছে যা এমন কোনও পাথের নাম পড়ে যা কোনও ট্যাব-বিস্মৃত ইনপুট ফাইলের প্রতিটি লাইন থেকে ফাঁকা স্থান এবং একটি স্কেলিং ফ্যাক্টর ধারণ করে allowed

while IFS=$t read -r path scale
do
    ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
done <recode-queue.txt

এই ক্ষেত্রে readবিল্টিন আইএফএসকে কেবল একটি ট্যাবে সেট করে দেখায়, সুতরাং এটি ফাঁকা জায়গায় পড়া ইনপুট লাইনটিকে বিভক্ত করবে না। কিন্তু কাজ IFS=$t set -- $lines করে না : কমান্ডটি কার্যকর করার আগে বিল্টিনের যুক্তি $linesতৈরি করার সাথে সাথে শেলটি প্রসারিত হয় , সুতরাং আইএফএসের অস্থায়ী বিন্যাসটি এমনভাবে নির্মিত যা কেবল বিল্টিনের সম্পাদনার সময় প্রযোজ্য late এই কারণেই আমি কোডগুলি স্নিপেটগুলি পৃথক ধাপে সমস্ত সেট আইএফএসের উপরে দিয়েছি এবং কেন এটি সংরক্ষণের বিষয়টি নিয়ে তাদের মোকাবেলা করতে হবে।set

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