বাশ স্ক্রিপ্টে কীভাবে / dev / stdout টার্গেটের অবস্থান সংরক্ষণ করবেন?


12

আমার একটি নির্দিষ্ট বাশ স্ক্রিপ্ট রয়েছে, যা /dev/stdout১ ম ফাইল বর্ণনাকারীকে অন্য অবস্থানের সাথে প্রতিস্থাপনের আগে মূল অবস্থানটি সংরক্ষণ করতে চায় ।

সুতরাং, স্বাভাবিকভাবেই, আমি এরকম কিছু লিখেছিলাম

old_stdout=$(readlink -f /dev/stdout)

এবং এটি কাজ করে না। সমস্যাটি খুব দ্রুত আমি বুঝতে পারি:

test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18

Obvioulsly, $()একটি সাবশেল চালায়, যা প্যারেন্ট শেলকে পাইপ করা হয়।

সুতরাং প্রশ্নটি: /dev/stdoutবাশ স্ক্রিপ্টের স্ট্রিং হিসাবে অবস্থান সংরক্ষণের জন্য কোনও নির্ভরযোগ্য (লিনাক্স বিতরণগুলির মধ্যে বহনযোগ্যতার জন্য প্রশস্ত) উপায় আছে কি?


এটি কিছুটা XY সমস্যার মতো মনে হচ্ছে । অন্তর্নিহিত সমস্যা কি?
কুসালানন্দ

অন্তর্নিহিত সমস্যাটি একটি নির্দিষ্ট ইনস্টলেশন স্ক্রিপ্ট যা দুটি মোডে চালিত হয় - নীরব, এতে ফাইল এবং ভার্বোজের জন্য এটি সমস্ত আউটপুট লগ করে, যাতে এটি কেবল ফাইল করতে লগইন করে না, সমস্ত কিছু টার্মিনালে মুদ্রণ করে। তবে উভয় মোডে স্ক্রিপ্টটি ব্যবহারকারীর সাথে ইন্ট্যারাক্ট করতে চায়, অর্থাত টার্মিনালে মুদ্রণ করতে হবে এবং ব্যবহারকারীর প্রতিক্রিয়া পড়বে। সুতরাং আমি ভেবেছিলাম যে সঞ্চয়টি /dev/stdoutসাইলেন্ট মোডে বার্তা মুদ্রণের মাধ্যমে সমস্যার সমাধান করবে। বিকল্প হ'ল আউটপুট উত্পাদন করে এমন প্রতিটি ক্রিয়া পুনঃনির্দেশিত হয় এবং সেগুলির বেশ কয়েকটি রয়েছে। ব্যবহারকারীর মিথস্ক্রিয়া বার্তাগুলির চেয়ে প্রায় 100 গুণ বেশি।
alexey.e.egorov

ব্যবহারকারীর সাথে কথোপকথনের মানক উপায়টি এতে মুদ্রণ করা stderr। এটি উদাহরণস্বরূপ, কেন অনুরোধগুলি stderrডিফল্টরূপে চলেছে ।
কুসালানন্দ

দুর্ভাগ্যক্রমে, stderrএছাড়াও অবশ্যই পুনঃনির্দেশিত এবং সংরক্ষণ করা উচিত, যেহেতু স্ক্রিপ্টটি বেশ কয়েকটি বহিরাগত প্রোগ্রামগুলি কল করে এবং সমস্ত সম্ভাব্য ত্রুটি বার্তাগুলি সংগ্রহ এবং লগ ইন করতে হয়।
alexey.e.egorov

উত্তর:


14

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

বোর্নের মতো শেল সহ অন্য কোনও ফাইলের বর্ণনা নকল করতে সিনট্যাক্সটি হ'ল:

exec 3>&1

উপরে, এফডি 1 এফডি 3 এ নকল করা হয়।

এর আগে যে কোনও এফডি 3 এর আগে খোলা ছিল তা বন্ধ হয়ে যাবে, তবে লক্ষ্য রাখবেন যে 3 থেকে 9 টি (সাধারণত আরও 99 টি পর্যন্ত yash) সংরক্ষণ করা হয় (এবং 0, 1, বা 2 এর বিপরীতে কোনও বিশেষ অর্থ নেই), শেল তাদের নিজের অভ্যন্তরীণ ব্যবসায়ের জন্য ব্যবহার না করতে জানে। Fd 3 এর আগেই কেবল খোলা থাকতে পারে কারণ আপনি এটি স্ক্রিপ্ট 1 এ করেছিলেন , বা এটি কলার দ্বারা ফাঁস হয়েছিল।

তারপরে, আপনি স্টাডাউটকে অন্য কোনও কিছুতে পরিবর্তন করতে পারেন:

exec > /dev/null

এবং পরে, stdout পুনরুদ্ধার করতে:

exec >&3 3>&-

( 3>&-ফাইল বিবরণী বন্ধ করার জন্য যা আমাদের আর প্রয়োজন নেই)।

এখন, এর সাথে সমস্যাটি হ'ল ksh ব্যতীত আপনার পরে চালানো প্রতিটি কমান্ড exec 3>&1সেই এফডি 3 এর উত্তরাধিকারী হবে That's এটি একটি এফডি ফুটো। সাধারণত কোনও বড় বিষয় নয়, তবে এটি সমস্যার কারণ হতে পারে।

kshসেট করে ক্লোজ-অন Exec ঐ fds থাকা ফ্ল্যাগ (2 উপর fds জন্য), কিন্তু অন্যান্য না শাঁস এবং অন্যান্য শাঁস যে পতাকা ম্যানুয়ালি সেট জন্য কোনো উপায় থাকে না।

অন্যান্য শেলের জন্য কাজটি প্রতিটি কমান্ডের জন্য fd 3 বন্ধ করা, যেমন:

exec 3>&-

exec > file.log

ls 3>&-
uname 3>&-

exec >&3 3>&-

কষ্টকর। এখানে, সর্বোত্তম উপায়টি হ'ল ব্যবহার না করা exec, তবে কমান্ড গোষ্ঠীগুলি পুনর্নির্দেশ করা:

{
  ls
  uname
} > file.log

সেখানে এটি শেলটি স্টাডাউট সংরক্ষণ এবং পরে পুনরুদ্ধার করতে যত্ন নেয় (এবং এটি অভ্যন্তরীণভাবে এফডি-তে ডুপ্লিকেট করে (9 এর উপরে, 99 এর উপরে yash) ক্লোজ-অন-এক্সিকিউট পতাকা সেট সহ)।

নোট 1

এখন, 3 থেকে 9 টি এইচডিএসের পরিচালনা করা জটিল এবং সমস্যাযুক্ত হতে পারে যদি আপনি এগুলি ব্যাপকভাবে বা ফাংশনগুলিতে ব্যবহার করেন, বিশেষত যদি আপনার স্ক্রিপ্টটিতে এমন কোনও তৃতীয় পক্ষের কোড ব্যবহার করা হয় যা পরিবর্তিতভাবে এফডিএস ব্যবহার করতে পারে।

কিছু শাঁস ( zsh, bash, ksh93, সমস্ত বৈশিষ্ট্য (যোগ এর অলিভার বেড়াজাল দ্বারা প্রস্তাবিতzsh 2005 সালে প্রায় একই সময়) পর তাদের বিকাশকারীদের মধ্যে আলোচনা করা হয়েছে) পরিবর্তে 10 যা এই ক্ষেত্রে সাহায্য করে উপরে প্রথম বিনামূল্যে FD দায়িত্ব অর্পণ করা একটি বিকল্প বাক্য গঠন আছে:

myfunction() {
  local fd
  exec {fd}>&1
  # stdout was duplicated onto a new fd above 10, whose actual value
  # is stored in the fd variable
  ...
  # it should even be safe to re-enter the function here
  ...
  exec >&"$fd" {fd}>&-
}

এছাড়াও, আপনার কোডটি এই অর্থে ভুল যে এফডি 3 ইতিমধ্যে গ্রহণ করা যেতে পারে, যখন কোনও rc.localসার্ভিস থেকে কোনও স্ক্রিপ্ট চালানো হয় তখন যেমন ঘটে থাকে , সুতরাং আপনি সত্যিই উচ্চস্বরে কিছু exec {FD}>&1বা অন্য কিছু ব্যবহার করেছেন । তবে এটি কেবল বাশ 4 এ সমর্থিত, যা সত্যই দুঃখজনক। সুতরাং এটি সত্যিই বহনযোগ্য নয়।
alexey.e.egorov

@ alexey.e.egorov, সম্পাদনা দেখুন।
স্টাফেন চেজেলাস

বাশ 3. * এই বৈশিষ্ট্যটিকে সমর্থন করে না, এবং এই সংস্করণটি সেন্টোস 5 এ ব্যবহৃত হয়, যা এখনও সমর্থিত এবং এখনও ব্যবহৃত হয়। এবং নিখরচায় বর্ণনাকারী সন্ধান করা এবং তারপরে eval "exec $i>&1"এমন একটি বিষয় যা আমি এড়াতে চাই, তার কারণ এটি জটিল। আমি কি 9 বছরের উপরে যে এফডিএসের উপর নির্ভর করতে পারি তা কি তখন নিখরচায় থাকবে?
alexey.e.egorov

@ অ্যালেক্সা.ই.ইগোরভ, না আপনি এটিকে পিছনে দেখছেন। 3 থেকে 9 টি এফডিএস বিনামূল্যে ব্যবহারের জন্য নিখরচায় রয়েছে (এবং এটি আপনার পছন্দমতো পরিচালনা করার জন্য আপনার উপর নির্ভরশীল) এবং সেই উদ্দেশ্যে তৈরি করা হয়। 9 বছরের উপরে এইচডিএস শেল দ্বারা অভ্যন্তরীণভাবে ব্যবহৃত হতে পারে এবং এগুলি বন্ধ করে দেওয়া খারাপ কাজ করতে পারে। বেশিরভাগ শেল আপনাকে সেগুলি ব্যবহার করতে দেয় না। bashআপনি নিজেকে পায়ে গুলি করতে দেবেন।
স্টাফেন চেজেলাস

2
@ অ্যালেক্সা.ই.ইগোরভ, আপনি যদি স্ক্রিপ্টের কিছুটা উদ্বোধন করেন (৩.৯.৯) খোলা থাকে, কারণ আপনার কলার তাদের বন্ধ করতে ভুলে গিয়েছিলেন বা তাদের উপরে এক্স-এক্স-এক্সিকিউট পতাকা সেট করতে ভুলে গেছেন। এটাই আমি এফডি ফাঁস বলেছি। এখন, সম্ভবত কলকারী আপনার কাছে এফডিএস পাঠানোর ইচ্ছা করেছিল, যাতে আপনি তাদের কাছে / থেকে ডেটা পড়তে এবং / অথবা লিখতে পারেন তবে তারপরে আপনি এটি সম্পর্কে জানতে পারবেন। আপনি যদি এগুলি সম্পর্কে জানেন না, তবে আপনার পাত্তা দেবে না, তবে আপনি সেগুলি নির্দ্বিধায় বন্ধ করতে পারেন (নোট করুন যে এটি কেবল আপনার লিখিতর পরিবর্তে আপনার স্ক্রিপ্টের প্রক্রিয়াটি fd বন্ধ করে দেয়)।
স্টাফেন চেজেলাস

3

আপনি দেখতে পাচ্ছেন, বাশ স্ক্রিপ্টিং কোনও নিয়মিত প্রোগ্রামিং ভাষার মতো নয় যেখানে আপনি ফাইল বর্ণনাকারী নির্ধারণ করতে পারেন।

সহজ সমাধানটি হ'ল আপনি যা যা পুনর্নির্দেশ করতে চান তা চালানোর জন্য একটি উপ-শেল ব্যবহার করা যাতে প্রক্রিয়াকরণটি শীর্ষ-শেলটিতে ফিরে যেতে পারে যার মান I / O অক্ষত রয়েছে।

একটি বিকল্প সমাধান ttyহ'ল টিটিওয়াই ডিভাইস সনাক্ত করতে এবং আপনার স্ক্রিপ্টে আই / ও নিয়ন্ত্রণ করতে। উদাহরণ স্বরূপ:

dev=$(tty)

এবং তারপর আপনি করতে পারেন ..

echo message > $dev

> একটি বিকল্প সমাধান হ'ল টিটিওয়াই ডিভাইস সনাক্ত করতে টিটি ব্যবহার করা এবং আপনার স্ক্রিপ্টে আই / ও নিয়ন্ত্রণ করতে হবে। কিভাবে এটি করে?
alexey.e.egorov

1
আমি আমার উত্তরে একটি উদাহরণ অন্তর্ভুক্ত করেছি।
জুলি পেলেটিয়ার

1

$$ ইন্টারেক্টিভ শেল বা স্ক্রিপ্ট প্রাসঙ্গিক শেল পিআইডি ক্ষেত্রে, আপনি বর্তমান প্রক্রিয়া পিআইডি পেতে হবে।

সুতরাং আপনি ব্যবহার করতে পারেন:

readlink -f /proc/$$/fd/1

উদাহরণ:

% readlink -f /proc/$$/fd/1
/dev/pts/33

% var=$(readlink -f /proc/$$/fd/1)

% echo $var                       
/dev/pts/33

1
যদিও এটি কার্যকরী, নির্দিষ্ট /procকাঠামোর উপর নির্ভর করা বহনযোগ্যতার সমস্যাগুলির কারণ /dev/stdoutহিসাবে প্রশ্নে উল্লিখিত হিসাবে ব্যবহার করে ।
জুলি পেলেটিয়ার

1
@ জুলিপেল্টিয়ার একটি নির্দিষ্ট /procকাঠামোর উপর নির্ভর করছেন ? এটি যে কোনও লিনাক্সে কাজ করবে procfs..
হিমাইল

1
ঠিক আছে, তাই আমরা procfsপ্রায় সর্বদা উপস্থিত হিসাবে লিনাক্সের জন্য সাধারণীকরণ করতে পারি , তবে আমরা প্রায়শই বহনযোগ্যতা প্রশ্নগুলি দেখতে পাই এবং একটি ভাল বিকাশের পদ্ধতিতে অন্যান্য সিস্টেমে বহনযোগ্যতার বিষয়টি বিবেচনা করা অন্তর্ভুক্ত। bashপ্রচুর অপারেটিং সিস্টেম চালাতে পারে।
জুলি পেলেটিয়ার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.