কমান্ড প্রতিস্থাপন: নতুন লাইনে বিভক্ত করা কিন্তু স্থান নয়


30

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

আমার মতো সামগ্রী সহ একটি ফাইল আছে

AAA
B C DDD
FOO BAR

যার মাধ্যমে আমি কেবলমাত্র এটি বোঝাতে চাইছি এটির বেশ কয়েকটি লাইন রয়েছে এবং প্রতিটি লাইনের স্পেস থাকতে পারে বা নাও থাকতে পারে। আমি একটি কমান্ড চালাতে চাই

cmd AAA "B C DDD" "FOO BAR"

আমি যদি ব্যবহার cmd $(< file)করি তবে আমি পাই

cmd AAA B C DDD FOO BAR

এবং আমি যদি ব্যবহার cmd "$(< file)"করি তবে আমি পাই

cmd "AAA B C DDD FOO BAR"

আমি প্রতিটি লাইনকে ঠিক এক প্যারামিটারের সাথে চিকিত্সা করব কীভাবে?


উত্তর:


26

Portably:

set -f              # turn off globbing
IFS='
'                   # split at newlines only
cmd $(cat <file)
unset IFS
set +f

অথবা IFSবিকল্পটি স্থানীয়ভাবে পরিবর্তনের জন্য সাবসেল ব্যবহার করে :

( set -f; IFS='
'; exec cmd $(cat <file) )

শেলটি একটি পরিবর্তনশীল বা কমান্ড প্রতিস্থাপনের ফলস্বরূপ ফিল্ড বিভাজন এবং ফাইলের নাম উত্পাদন করে যা ডাবল উদ্ধৃতিতে নেই। সুতরাং আপনাকে কেবলমাত্র নতুন লাইনের পৃথক ক্ষেত্র তৈরি করতে ফাইল নাম জেনারেশনটি বন্ধ করতে হবে set -fএবং ফিল্ড বিভাজনকে কনফিগার IFSকরতে হবে।

বাশ বা কেএস কনস্ট্রাক্টসের সাহায্যে লাভ করার মতো অনেক কিছুই নেই। আপনি IFSএকটি ফাংশন স্থানীয় করতে পারেন , কিন্তু না set -f

ব্যাশ বা ksh93 এ, আপনি যদি একাধিক কমান্ডে তাদের পাস করার প্রয়োজন হয় তবে আপনি ক্ষেত্রগুলি একটি অ্যারেতে সঞ্চয় করতে পারেন। অ্যারে তৈরি করার সময় আপনাকে সম্প্রসারণ নিয়ন্ত্রণ করতে হবে। তারপরে "${a[@]}"অ্যারের উপাদানগুলিতে প্রসারিত হয়, প্রতি শব্দ প্রতি একটি করে।

set -f; IFS=$'\n'
a=($(cat <file))
set +f; unset IFS
cmd "${a[@]}"

10

আপনি একটি অস্থায়ী অ্যারে দিয়ে এটি করতে পারেন।

সেটআপ:

$ cat input
AAA
A B C
DE F
$ cat t.sh
#! /bin/bash
echo "$1"
echo "$2"
echo "$3"

অ্যারে পূরণ করুন:

$ IFS=$'\n'; set -f; foo=($(<input))

অ্যারে ব্যবহার করুন:

$ for a in "${foo[@]}" ; do echo "--" "$a" "--" ; done
-- AAA --
-- A B C --
-- DE F --

$ ./t.sh "${foo[@]}"
AAA
A B C
DE F

অস্থায়ী পরিবর্তনশীল ব্যতীত এটি করার কোনও উপায় খুঁজে বের করতে পারে না - যদি না এই ক্ষেত্রে IFSপরিবর্তনটি গুরুত্বপূর্ণ না হয় তবে cmd:

$ IFS=$'\n'; set -f; cmd $(<input) 

এটা করা উচিত।


IFSসর্বদা আমাকে বিভ্রান্ত করে তোলে IFS=$'\n' cmd $(<input)কাজ করে না IFS=$'\n'; cmd $(<input); unset IFSকাজ করে কেন? আমার ধারণা আমি ব্যবহার করব(IFS=$'\n'; cmd $(<input))
ওল্ড প্রো

6
@ ওल्डপ্রো IFS=$'\n' cmd $(<input)কাজ করে না কারণ এটি শুধুমাত্র IFSপরিবেশের মধ্যে সেট করে cmd$(<input)অ্যাসাইনমেন্টটি IFSসম্পাদন করার আগে কমান্ডটি তৈরি করতে প্রসারিত হয় ।
গিলস 'অসন্তুষ্ট হওয়া বন্ধ করুন'

8

মনে হচ্ছে এটি করার ক্যানোনিকাল উপায়টি এরকম bashকিছু

unset args
while IFS= read -r line; do 
    args+=("$line") 
done < file

cmd "${args[@]}"

বা, যদি আপনার বাশের সংস্করণটি থাকে mapfile:

mapfile -t args < filename
cmd "${args[@]}"

ওপলাইনার বনাম ম্যাপফাইলে এবং পঠনযোগ্য লুপের মধ্যে আমি খুঁজে পাচ্ছি

(set -f; IFS=$'\n'; cmd $(<file))

এটি হ'ল প্রাক্তন একটি ফাঁকা লাইনটি একটি খালি যুক্তিতে রূপান্তরিত করবে, যখন ওয়ান-লাইনার একটি ফাঁকা রেখা উপেক্ষা করবে। এক্ষেত্রে ওয়ান-লাইনারের আচরণটি আমি যাহাই হউক না কেন পছন্দ করি, তাই এতে কমপ্যাক্ট হওয়ার কারণে ডাবল বোনাস।

আমি ব্যবহার করব IFS=$'\n' cmd $(<file)তবে এটি কাজ করে না, কারণ কার্যকর $(<file)হওয়ার আগে কমান্ড লাইন গঠনের ব্যাখ্যা দেওয়া হয় IFS=$'\n'

যদিও এটি আমার ক্ষেত্রে কাজ করে না, আমি এখন শিখেছি যে প্রচুর সরঞ্জামগুলি লাইনটি টার্মিনেটিংয়ের null (\000)পরিবর্তে সমর্থন করে যার পরিবর্তে newline (\n), ফাইলের নামগুলি ব্যবহার করার সময় এইগুলি অনেক সহজ করে তোলে যা এই পরিস্থিতিতেগুলির সাধারণ উত্স are :

find / -name '*.config' -print0 | xargs -0 md5

কোনও গ্লাবিং বা ইন্টারপোলটিং বা যা কিছু না করে এমডি 5-তে আর্গুমেন্ট হিসাবে সম্পূর্ণ-যোগ্য ফাইল নামের একটি তালিকা ফিড করে। এটি অন্তর্নির্মিত সমাধানের দিকে নিয়ে যায়

tr "\n" "\000" <file | xargs -0 cmd

যদিও এটি, খালি লাইনগুলিকেও উপেক্ষা করে, যদিও এটি কেবল সাদা অংশ রয়েছে এমন লাইনগুলি ক্যাপচার করে।


cmd $(<file)উদ্ধৃতি না দিয়ে মান ব্যবহার করা (শব্দের বিভক্ত করার জন্য ব্যাশের ক্ষমতা ব্যবহার করা) সর্বদা একটি ঝুঁকিপূর্ণ বাজি। কোনও লাইন থাকলে *এটি শেল দ্বারা ফাইলগুলির তালিকায় প্রসারিত হবে।

3

আপনি mapfileঅ্যারেতে ফাইলটি পড়তে অন্তর্নির্মিত ব্যাশটি ব্যবহার করতে পারেন

mapfile -t foo < filename
cmd "${foo[@]}"

বা, অনির্ধারিত, xargsএটি করতে পারে

xargs cmd < filename

ম্যাপফাইলে ডকুমেন্টেশন থেকে: "ম্যাপফাইল কোনও সাধারণ বা বহনযোগ্য শেল বৈশিষ্ট্য নয়"। এবং সত্যিই এটি আমার সিস্টেমে সমর্থিত নয়। xargsহয় না, সাহায্য করে না।
ওল্ড প্রো

আপনার প্রয়োজন হবে xargs -dবাxargs -L
জেমস ইয়ংম্যান

@ জেমস, না, আমার কাছে কোনও -dবিকল্প নেই এবং xargs -L 1প্রতি লাইনে একবার কমান্ড চালানো হয় তবে তবুও হোয়াইটস্পেসে আর্গিটগুলি বিভক্ত করা হয়।
ওল্ড প্রো

1
@ ওल्डপ্রো, ভাল আপনি "সাধারণ বা পোর্টেবল শেল বৈশিষ্ট্য" এর পরিবর্তে "কেবলমাত্র বিল্ট-ইনগুলি ব্যবহার করে এটি করার উপায়" চেয়েছিলেন। আপনার বাশ সংস্করণটি যদি খুব পুরানো হয় তবে আপনি কি এটি আপডেট করতে পারবেন?
গ্লেন জ্যাকম্যান 28'12

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

0
old=$IFS
IFS='  #newline
'
array=`cat Submissions` #input the text in this variable
for ...  #use parts of variable in the for loop
... 
done
IFS=$old

আমি খুঁজে পেতে পারে সবচেয়ে ভাল উপায়। জাস্ট ওয়ার্কস


এবং আপনি যদি IFSস্থান নির্ধারণ করে থাকেন তবে এটি কেন কাজ করে তবে প্রশ্নটি স্থানটিতে বিভক্ত না হওয়ার জন্য?
র‌্যালফ্রেডল

0

ফাইল

নতুন লাইনে কোনও ফাইলকে বিভক্ত করার জন্য সবচেয়ে বেসিক লুপ (পোর্টেবল) হ'ল:

#!/bin/sh
while read -r line; do            # get one line (\n) at a time.
    set -- "$@" "$line"           # store in the list of positional arguments.
done <infile                      # read from a file called infile.
printf '<%s>' "$@" ; echo         # print the results.

যা মুদ্রণ করবে:

$ ./script
<AAA><A B C><DE F>

হ্যাঁ, ডিফল্ট আইএফএস = দিয়ে spacetabnewline

কেন এটি কাজ করে

  • আইএফএস শেল দ্বারা ইনপুটটিকে কয়েকটি ভেরিয়েবলগুলিতে বিভক্ত করতে ব্যবহৃত হবে । যেহেতু শুধুমাত্র একটি ভেরিয়েবল রয়েছে শেল দ্বারা কোনও বিভাজন সঞ্চালিত হয় না। সুতরাং, প্রয়োজনের কোন পরিবর্তন IFS
  • হ্যাঁ, শীর্ষস্থানীয় এবং পিছনের স্থান / ট্যাবগুলি সরানো হচ্ছে, তবে এটি এক্ষেত্রে কোনও সমস্যা বলে মনে হচ্ছে না।
  • না, না globbing কোন সম্প্রসারণ করা হয় সম্পন্ন করা হয় unquoted । সুতরাং, কোন set -fপ্রয়োজন নেই।
  • একমাত্র অ্যারে ব্যবহৃত (বা প্রয়োজনীয়) হ'ল অ্যারের মতো অবস্থানগত পরামিতি।
  • -r(কাঁচা) বিকল্প সবচেয়ে ব্যাকস্ল্যাশ অপসারণের এড়াতে হয়।

বিভাজন এবং / বা গ্লোববিংয়ের প্রয়োজন হলে এটি কাজ করবে না । এই জাতীয় ক্ষেত্রে আরও জটিল কাঠামোর প্রয়োজন।

আপনার যদি প্রয়োজন (এখনও বহনযোগ্য):

  • শীর্ষস্থানীয় এবং পিছনের স্থান / ট্যাবগুলি অপসারণ এড়িয়ে চলুন, ব্যবহার করুন: IFS= read -r line
  • কিছু চরিত্র, ব্যবহারের উপর Vars করার স্প্লিট লাইন: IFS=':' read -r a b c

ফাইলটি অন্য কোনও চরিত্রে বিভক্ত করুন (পোর্টেবল নয়, ksh, বাশ, zsh এর সাথে কাজ করে):

IFS=':' read -d '+' -r a b c

সম্প্রসারণ

অবশ্যই, আপনার প্রশ্নের শিরোনাম স্থানগুলিতে বিভাজন এড়ানো নিউলাইনগুলিতে একটি কমান্ড প্রয়োগের বিভাজন সম্পর্কে।

শেল থেকে বিভক্ত হওয়ার একমাত্র উপায় হ'ল উদ্ধৃতি ছাড়াই একটি প্রস্থান ছেড়ে যাওয়া:

echo $(< file)

এটি আইএফএসের মান দ্বারা নিয়ন্ত্রিত হয় এবং অব্যক্ত বিস্তৃতিতে গ্লোববিং প্রয়োগ করা হয়। সেই কাজটি এমকেএ করতে আপনার প্রয়োজন:

  • শুধুমাত্র নতুন লাইনে বিভাজন পেতে শুধুমাত্র আইএফএসকে নতুন লাইনে সেট করুন ।
  • গ্লোব্বিং শেল বিকল্পটি আনসেট করুন set +f:

    সেট + f আইএফএস = '' সেমিডি $ (<ফাইল)

অবশ্যই, এটি স্ক্রিপ্টের বাকী অংশের জন্য আইএফএস এবং গ্লোববিংয়ের মান পরিবর্তন করে।

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