বাশে শেষ কমান্ড চালানোর প্রতিধ্বনি?


84

আমি ব্যাশ স্ক্রিপ্টের মধ্যে শেষ কমান্ডটি চালানোর চেষ্টা করছি। history,tail,head,sedকমান্ডগুলি আমার স্ক্রিপ্টের একটি পার্সার দৃষ্টিকোণ থেকে একটি নির্দিষ্ট লাইন উপস্থাপন করে এমন কিছু নিয়ে এটি করার একটি উপায় খুঁজে পেয়েছি যা সূক্ষ্ম হয়। তবে কিছু পরিস্থিতিতে আমি প্রত্যাশিত আউটপুটটি পাই না, উদাহরণস্বরূপ যখন একটি caseবিবৃতিতে কমান্ডটি সন্নিবেশ করা হয় :

এই পান্ডুলিপি:

#!/bin/bash
set -o history
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"

case "1" in
  "1")
  date
  last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
  echo "last command is [$last]"
  ;;
esac

আউটপুট:

Tue May 24 12:36:04 CEST 2011
last command is [date]
Tue May 24 12:36:04 CEST 2011
last command is [echo "last command is [$last]"]

[প্রশ্ন] বাশ স্ক্রিপ্টের মধ্যে এই কমান্ডটি কীভাবে / যেখানে বলা হয়েছে তা নির্বিশেষে কেউ কি আমাকে সর্বশেষ রান কমান্ড প্রতিধ্বনির উপায় খুঁজতে সাহায্য করতে পারে?

আমার উত্তর

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

উদাহরণ লিপি:

#!/bin/bash
die() { echo >&2 -e "\nERROR: $@\n"; exit 1; }
run() { "$@"; code=$?; [ $code -ne 0 ] && die "command [$*] failed with error code $code"; }

case "1" in
  "1")
  run ls /opt
  run ls /wrong-dir
  ;;
esac

আউটপুট:

$ ./test.sh
apacheds  google  iptables
ls: cannot access /wrong-dir: No such file or directory

ERROR: command [ls /wrong-dir] failed with error code 2

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


+1, উজ্জ্বল ধারণা! নোট তবে যে run()সঠিকভাবে কাজ করে না যখন কোট ব্যবহার করা হয়, উদাহরণস্বরূপ এই ব্যর্থ হলে: run ssh-keygen -t rsa -C info@example.org -f ./id_rsa -N ""
জোহানোদো

@ জোহান্দো: এটি সংশোধন করা যেতে পারে: প্রয়োজনের "something"সাথে প্রথম স্তরে '"something"'(বা পরিবর্তে, ভেরিয়েবলগুলি) ব্যাখ্যা / মূল্যায়ন "'something'"করার অনুমতি দেওয়ার সাথে যুক্তিতে পরিবর্তন করুনsomething
অলিভিয়ার দুলাক

4
আমি ভ্রান্তকে run() { $*; … }আরও প্রায় সঠিক হিসাবে পরিবর্তন করেছি run() { "$@"; … }কারণ ভুল উত্তরটি শেষ হয়েছে প্রশ্নটি cp64৪ টি ত্রুটিযুক্ত স্থিতি দিয়ে বেরিয়েছে , যেখানে সমস্যাটি হ'ল $*নামগুলির ফাঁকা জায়গাগুলিতে কমান্ড যুক্তিগুলি ভেঙেছিল, কিন্তু "$@"তা করবে না।
জোনাথন লেফলার

ইউনিক্স স্ট্যাকএক্সচেঞ্জ সম্পর্কিত সম্পর্কিত প্রশ্ন: unix.stackexchange.com/questions/21930/…
haridsv

last=$(history | tail -n1 | sed 's/^[[:space:]][0-9]*[[:space:]]*//g')কমপক্ষে zsh এবং
macOS

উত্তর:


60

কমান্ডের ইতিহাসটি একটি ইন্টারেক্টিভ বৈশিষ্ট্য। ইতিহাসে কেবলমাত্র সম্পূর্ণ কমান্ড প্রবেশ করা হয়। উদাহরণস্বরূপ, caseশেলটি পার্সিং শেষ করার পরে কনস্ট্রাক্টটি পুরো হিসাবে প্রবেশ করা হয়। ইতিহাস historyঅন্তর্নির্মিত (বা শেল এক্সপেনশন ( !:p) দ্বারা এটি মুদ্রণ ) দিয়ে আপনি অনুসন্ধানগুলি দেখতে চান তা করেন না, যা সাধারণ কমান্ডের অনুরোধ মুদ্রণ করা।

DEBUGফাঁদ যদি আপনি কোন সহজ কমান্ড সঞ্চালনের আগে একটি কমান্ড অধিকার চালানোর সুযোগ দিবে। কার্যকর করতে কমান্ডের একটি স্ট্রিং সংস্করণ (স্পেস দ্বারা বিচ্ছিন্ন শব্দের সাথে) চলকটিতে উপলভ্য BASH_COMMAND

trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
…
echo "last command is $previous_command"

মনে রাখবেন যে previous_commandআপনি যখনই কোনও কমান্ড চালাবেন ততবার পরিবর্তন হবে, সুতরাং এটি ব্যবহারের জন্য এটি একটি ভেরিয়েবলে সংরক্ষণ করুন। আপনি যদি পূর্ববর্তী কমান্ডের রিটার্নের স্ট্যাটাসটিও জানতে চান তবে উভয়টিকে একটি একক কমান্ডে সংরক্ষণ করুন।

cmd=$previous_command ret=$?
if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fi

তদ্ব্যতীত, আপনি যদি কেবলমাত্র একটি ব্যর্থ কমান্ডকেই বাতিল set -eকরতে চান তবে আপনার স্ক্রিপ্টটি প্রথম ব্যর্থ কমান্ডে প্রস্থান করতে ব্যবহার করুন। আপনি EXITফাঁদ থেকে শেষ কমান্ডটি প্রদর্শন করতে পারেন ।

set -e
trap 'echo "exit $? due to $previous_command"' EXIT

মনে রাখবেন যে আপনি যদি আপনার স্ক্রিপ্টটি কী করছে তা দেখার জন্য যদি এটি চেষ্টা করে থাকেন তবে এই সব ভুলে গিয়ে ব্যবহার করুন set -x


4
আমি আপনার ডিইউবিজি ফাঁদ চেষ্টা করেছি কিন্তু আমি এটি কাজ করতে পারি না, দয়া করে আপনি একটি পুরো উদাহরণ সরবরাহ করতে পারেন? -xপ্রতিটি একক কমান্ড আউটপুট দেয় তবে দুর্ভাগ্যক্রমে আমি কেবল ব্যর্থ হওয়া কমান্ডগুলি দেখতে আগ্রহী (যা আমি আমার আদেশের সাথে অর্জন করতে পারি যদি আমি এটি একটি [ ! "$? == "0" ]বিবৃতিতে রাখি ।
সর্বোচ্চ

@ ব্যবহারকারী 359650: স্থির। পূর্ববর্তী কমান্ডটি বর্তমান কমান্ড দ্বারা ওভাররাইট করার আগে আপনার সংরক্ষণ করা উচিত। আপনার স্ক্রিপ্টটি বাতিল করতে যদি কোনও কমান্ড ব্যর্থ হয় set -eতবে ব্যবহার করুন (প্রায়শই, তবে সর্বদা নয়, কমান্ডটি যথেষ্ট পরিমাণে যথেষ্ট ত্রুটি বার্তা উত্পন্ন করবে যাতে আপনার আরও প্রসঙ্গ সরবরাহ করার দরকার নেই)।
গিলস 'অসন্তুষ্ট হওয়া বন্ধ করুন'

আপনার অবদানের জন্য ধন্যবাদ আপনার সমাধানটি খুব বেশি ওভারহেড হওয়ায় আমি একটি কাস্টম ফাংশন (আমার পোস্ট দেখুন) লেখা শেষ করেছি।
সর্বাধিক

আশ্চর্যজনক কৌশল। সংজ্ঞা +1। আমার কাছে সেট-ই অংশ এবং ইআরআর ফাঁদ ছিল, আপনি আমাকে ডিইবিগ অংশ দিয়েছেন। অনেক ধন্যবাদ!
ফিলিপ এ।

4
@ জেমস থমাস মুন ১৯৯৯ সাধারণভাবে eval echo "${BASH_COMMAND}"কমান্ডের বিকল্পগুলিতে স্বেচ্ছাসেবক কোড কার্যকর করতে পারে। এটা বিপদজনক. যেমন একটি কমান্ড বিবেচনা করুন cd $(ls -td | head -n 1)- এবং এখন আদেশ কমান্ড বিকল্প rmবা কল্পনা কিছু।
গিলস 'তাই খারাপ হওয়া বন্ধ করুন'

174

মৃত্যুদন্ড কার্যকর করা শেষ কমান্ডটি অ্যাক্সেস করতে বাশ বৈশিষ্ট্যগুলি তৈরি করেছে। তবে এটি সর্বশেষ সম্পূর্ণ কমান্ড (যেমন পুরো caseকমান্ড), আপনার মতো আলাদা আলাদা সাধারণ কমান্ডগুলি মূলত অনুরোধ করা হয়নি।

!:0 কমান্ডের নাম কার্যকর করা হয়েছে।

!:1 = পূর্ববর্তী কমান্ডের প্রথম পরামিতি

!:* পূর্ববর্তী কমান্ডের সমস্ত পরামিতি

!:-1 = পূর্ববর্তী কমান্ডের চূড়ান্ত প্যারামিটার

!! = পূর্ববর্তী কমান্ড লাইন

ইত্যাদি

সুতরাং, প্রশ্নের সহজ উত্তরটি আসলে:

echo !!

... বিকল্পভাবে:

echo "Last command run was ["!:0"] with arguments ["!:*"]"

এটি নিজে চেষ্টা করো!

echo this is a test
echo !!

কোনও স্ক্রিপ্টে, ইতিহাসের সম্প্রসারণটি ডিফল্টরূপে বন্ধ থাকে, আপনাকে এটি সক্ষম করতে হবে

set -o history -o histexpand

8
সর্বাধিক কার্যকর ব্যবহারের কেস আমি দেখেছি হ'ল সুডো অ্যাক্সেস সহ শেষ কমান্ডটি পুনরায় চালানো , অর্থাৎsudo !!
ট্র্যাভেস্টি 3

4
সঙ্গে set -o history -o histexpand; echo "!!": একটি ব্যাশ স্ক্রিপ্টের মধ্যে আমি এখনও ত্রুটি বার্তা পেতে !!: event not found(এটা উদ্ধৃতি চিহ্ন ছাড়া একই।)
Suzana

4
set -o history -o histexpandস্ক্রিপ্টগুলিতে -> জীবনকাল! ধন্যবাদ!
আলবার্তো মেগিয়া

টাইম ফাংশন দ্বারা ব্যবহৃত টাইমফর্ম্যাট স্ট্রিংয়ে এই আচরণটি পাওয়ার কোনও উপায় আছে কি? অর্থাত TIMEFORMAT = "***!: 0%% 0LR নিয়েছে" রফতানি করেছে; / usr / bin / time -name "* .log" ... যা কাজ করে না কারণ আপনি রফতানি চালানোর সময়!: 0 মূল্যায়ন করা হয় :(
মার্টিন

আমাকে হিস্টিপেক্সে সেট-ই ইতিহাস ব্যবহার সম্পর্কে আরও পড়তে হবে। আমি যে ফাইলটিতে কল করি তাতে এটির আমার ব্যবহার bashমুদ্রণ চালিয়ে যায় !! শেষ রান কমান্ডের পরিবর্তে। এই নথি কোথায়?
মুনো

17

গিলসের কাছ থেকে উত্তর পড়ার পরে , আমি সিদ্ধান্ত নিয়েছি যে কোনও ফাঁদে ভেরটিও পাওয়া যায় (এবং কাঙ্ক্ষিত মান) - এবং এটি!$BASH_COMMANDEXIT

সুতরাং, নিম্নলিখিত বাশ স্ক্রিপ্ট প্রত্যাশার মতো কাজ করে:

#!/bin/bash

exit_trap () {
  local lc="$BASH_COMMAND" rc=$?
  echo "Command [$lc] exited with code [$rc]"
}

trap exit_trap EXIT
set -e

echo "foo"
false 12345
echo "bar"

আউটপুট হয়

foo
Command [false 12345] exited with code [1]

barকখনই মুদ্রিত হয় না কারণ set -eকোনও আদেশ যখন ব্যর্থ হয় এবং মিথ্যা কমান্ড সর্বদা ব্যর্থ হয় (সংজ্ঞা অনুসারে) স্ক্রিপ্ট থেকে বেরিয়ে আসে বাশকে। 12345পাশ থেকে falseশুধু আছে দেখাতে হবে যে ব্যর্থ কমান্ড আর্গুমেন্ট পাশাপাশি দখল করা হয় ( falseকমান্ড এটি পাস কোনো আর্গুমেন্ট উপেক্ষা করে)


এটি একেবারে সেরা সমাধান। "সেট -ইউও পাইপফেইল" নিয়ে আমার জন্য আকর্ষণীয় কাজ করে
ভুকাসিন

8

আমি set -xমূল স্ক্রিপ্টে ব্যবহার করে এটি অর্জন করতে সক্ষম হয়েছি (যা স্ক্রিপ্টটি কার্যকর করে দেওয়া প্রতিটি কমান্ডকে মুদ্রণ করে তোলে) এবং একটি মোড়ক স্ক্রিপ্ট লিখে যা কেবলমাত্র আউটপুট তৈরির শেষ লাইনটি দেখায় set -x

এটি মূল লিপি:

#!/bin/bash
set -x
echo some command here
echo last command

এবং এটি মোড়ক স্ক্রিপ্ট:

#!/bin/sh
./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //'

মোড়ক স্ক্রিপ্ট চালানো আউটপুট হিসাবে এটি উত্পাদন করে:

echo last command

3

history | tail -2 | head -1 | cut -c8-999

tail -2ইতিহাস থেকে শেষ দুটি কমান্ড লাইন head -1প্রত্যাবর্তন করে ঠিক প্রথম লাইনে cut -c8-999পিআইডি এবং স্পেসগুলি সরিয়ে কেবলমাত্র কমান্ড লাইন দেয়।


4
কমান্ড আর্গুমেন্ট কি কি আপনি একটি সামান্য ব্যাখ্যা করতে পারেন? এটি আপনি কী করেছেন তা বুঝতে সহায়তা করবে
সিগ্রিস্ট

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

1

শেষ কমান্ড ($ _) এবং শেষ ত্রুটি ($?) ভেরিয়েবলের মধ্যে একটি রেসকন্ডিশন রয়েছে। যদি আপনি তাদের মধ্যে একটি নিজস্ব ভেরিয়েবলে সঞ্চয় করার চেষ্টা করেন তবে সেট কমান্ডের কারণে উভয়ই ইতিমধ্যে নতুন মানগুলির মুখোমুখি হয়েছিল। আসলে, শেষ কমান্ড এই ক্ষেত্রে মোটেই কোন মান পায় নি।

আমি এখানে উভয় তথ্য নিজের ভেরিয়েবলগুলিতে সঞ্চয় করতে (প্রায়) যা করেছি তা হ'ল আমার বাশ স্ক্রিপ্টটি নির্ধারণ করতে পারে যে কোনও ত্রুটি ছিল কিনা এবং শেষ রান কমান্ডের সাথে শিরোনাম নির্ধারণ করে:

   # This construct is needed, because of a racecondition when trying to obtain
   # both of last command and error. With this the information of last error is
   # implied by the corresponding case while command is retrieved.

   if   [[ "${?}" == 0 && "${_}" != "" ]] ; then
    # Last command MUST be retrieved first.
      LASTCOMMAND="${_}" ;
      RETURNSTATUS='✓' ;
   elif [[ "${?}" == 0 && "${_}" == "" ]] ; then
      LASTCOMMAND='unknown' ;
      RETURNSTATUS='✓' ;
   elif [[ "${?}" != 0 && "${_}" != "" ]] ; then
    # Last command MUST be retrieved first.
      LASTCOMMAND="${_}" ;
      RETURNSTATUS='✗' ;
      # Fixme: "$?" not changing state until command executed.
   elif [[ "${?}" != 0 && "${_}" == "" ]] ; then
      LASTCOMMAND='unknown' ;
      RETURNSTATUS='✗' ;
      # Fixme: "$?" not changing state until command executed.
   fi

এই স্ক্রিপ্টটি কোনও ত্রুটি ঘটলে এবং তথ্যটি সর্বশেষ রক্ষণ কমান্ডটি প্রাপ্ত করে রাখে। রেসকন্ডিশনের কারণে আমি আসল মান সঞ্চয় করতে পারি না। এছাড়াও, বেশিরভাগ কমান্ডগুলি আসলে ত্রুটির নাম্বারগুলির জন্যও যত্ন করে না, তারা কেবল '0' থেকে আলাদা কিছু দেয়। আপনি খেয়াল করবেন, যদি আপনি বাশের ইরোনো এক্সটেনশন ব্যবহার করেন।

ব্যাশের জন্য "ইন্টার্ন" স্ক্রিপ্টের মতো, যেমন বাশ এক্সটেনশনের মতো এটিও সম্ভব হওয়া উচিত, তবে আমি এর মতো কোনও কিছুর সাথে পরিচিত নই এবং এটিও উপযুক্ত নয়।

সংশোধন

আমি ভাবিনি, একই সাথে উভয় ভেরিয়েবল পুনরুদ্ধার করা সম্ভব হয়েছিল। যদিও আমি কোডটির স্টাইলটি পছন্দ করি, আমি ধরে নিয়েছিলাম এটি দুটি কমান্ড হিসাবে ব্যাখ্যা করা হবে। এটি ভুল ছিল, সুতরাং আমার উত্তরটি এতে ভাগ করে দেয়:

   # Because of a racecondition, both MUST be retrieved at the same time.
   declare RETURNSTATUS="${?}" LASTCOMMAND="${_}" ;

   if [[ "${RETURNSTATUS}" == 0 ]] ; then
      declare RETURNSYMBOL='✓' ;
   else
      declare RETURNSYMBOL='✗' ;
   fi

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


4
ওহে প্রিয়, আপনি কেবল একবারে এগুলি গ্রহণ করতে পেরেছেন এবং এটি সম্ভব হওয়ার জন্য এই SEEMS: ঘোষণা করুন রিটার্নস্ট্যাটাস = "$ {?}" লাস্টকোম্যান্ড = "$ {_}";
ডাব্লুজিআরএম

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