কেন সেট -e একটি OR তালিকা অনুসারে প্রথম বন্ধনী () এর সাথে সাব-শেলগুলির অভ্যন্তরে কাজ করবে না ||


30

আমি সম্প্রতি এর মতো কিছু স্ক্রিপ্টিং চালিয়েছি:

( set -e ; do-stuff; do-more-stuff; ) || echo failed

এটি আমার কাছে দুর্দান্ত দেখাচ্ছে তবে এটি কার্যকর হয় না! এটি যুক্ত set -eহয় না, যখন আপনি যুক্ত করেন ||। তা ছাড়া, এটি সূক্ষ্মভাবে কাজ করে:

$ ( set -e; false; echo passed; ); echo $?
1

যাইহোক, যদি আমি যোগ ||, set -eউপেক্ষা করা হয়:

$ ( set -e; false; echo passed; ) || echo failed
passed

একটি বাস্তব, পৃথক শেল প্রত্যাশা অনুযায়ী কাজ করে:

$ sh -c 'set -e; false; echo passed;' || echo failed
failed

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


`(....)` `কনস্ট্রাক্টটি এর সামগ্রীগুলি চালনার জন্য পৃথক শেল শুরু করে, এতে কোনও সেটিংস বাইরে প্রয়োগ হয় না।
ভোনব্র্যান্ড

@ ভনব্র্যান্ড, আপনি বিন্দুটি মিস করেছেন। তিনি চান যে এটি সাবশেলের অভ্যন্তরে প্রয়োগ করা হোক, তবে ||বাইরে সাবসেলের মাধ্যমে সাব-শেলের অভ্যন্তর আচরণকে প্রভাবিত করে।
সিজেএম

1
তুলনা (set -e; echo 1; false; echo 2)সঙ্গে(set -e; echo 1; false; echo 2) || echo 3
জোহান

উত্তর:


32

এই থ্রেড অনুসারে , এটি পসিক্স আচরণটি set -eএকটি সাবসেলে " " ব্যবহারের জন্য নির্দিষ্ট করে ।

(আমিও অবাক হয়েছি।)

প্রথম, আচরণ:

-eযৌগিক তালিকাটি কার্যকর করার পরে সেটিংসটিকে অগ্রাহ্য করা হবে যতক্ষণ না, যদি, বা এলিফ সংরক্ষিত শব্দ না হওয়া পর্যন্ত পাইপলাইনটি শুরু হয়! সংরক্ষিত শব্দ, বা শেষের ব্যতীত কোনও AND-OR তালিকার কোনও আদেশ।

দ্বিতীয় পোস্ট নোট,

সংক্ষেপে, সেট-ই (সাব-কোড কোড) পার্শ্ববর্তী প্রসঙ্গে স্বাধীনভাবে কাজ করা উচিত নয়?

না। পসিক্সের বর্ণনাটি পরিষ্কার যে পার্শ্ববর্তী প্রসঙ্গটি সেট-ই সাব-শেল উপেক্ষা করে কিনা তা প্রভাবিত করে।

চতুর্থ পোস্টে আরও কিছু রয়েছে, এরিক ব্লেকেরও,

পয়েন্ট 3 না subshells প্রয়োজন প্রেক্ষিতে যেখানে ওভাররাইড করতে set -eউপেক্ষা করা হয়। এটি হ'ল একবার আপনি যখন এমন প্রসঙ্গে চলে যান যেখানে -eউপেক্ষা করা হয়, আবার বাধ্য হয়ে যাবার জন্য আপনি কিছুই করতে পারবেন না -e, এমনকি সাবসেলও নয়।

$ bash -c 'set -e; if (set -e; false; echo hi); then :; fi; echo $?' 
hi 
0 

যদিও আমরা set -eদু'বার ফোন করেছি (পিতা-মাতা এবং সাব-শেল উভয় ক্ষেত্রে), সাব-শেল এমন একটি প্রসঙ্গে উপস্থিত রয়েছে যেখানে -eউপেক্ষা করা হয়েছে (যদি বিবৃতি দেওয়ার শর্ত) তবে পুনরায় সক্ষম করার জন্য সাব-শেল-তে আমরা কিছুই করতে পারি না fact -e

এই আচরণ অবশ্যই অবাক। এটি পাল্টা স্বজ্ঞাত: একটি পুনরায় সক্রিয় set -eকরার একটি প্রভাব ফেলতে পারে আশা করে এবং পার্শ্ববর্তী প্রসঙ্গটি নজরে না নেয়; আরও, পসিক্স স্ট্যান্ডার্ডের শব্দটি এটি বিশেষভাবে পরিষ্কার করে না। কমান্ডটি ব্যর্থ হচ্ছে এমন প্রসঙ্গে আপনি যদি এটি পড়েন তবে বিধিটি প্রযোজ্য নয়: এটি কেবল আশেপাশের প্রসঙ্গে প্রযোজ্য, তবে এটি এটি সম্পূর্ণরূপে প্রযোজ্য।


এই লিঙ্কগুলির জন্য ধন্যবাদ, তারা খুব আকর্ষণীয় ছিল। তবে, আমার উদাহরণটি (আইএমও) যথেষ্ট আলাদা। সেই আলোচনার বেশিরভাগটি পিতামাতার শেলের সেট-ই সাব-শেল দ্বারা উত্তরাধিকার সূত্রে প্রাপ্ত কিনা set -e; (false; echo passed;) || echo failed। এটি আমাকে অবাক করে না, প্রকৃতপক্ষে - এটি স্ট্যান্ডার্ডের শব্দটি দেওয়াতে এই ক্ষেত্রে উপেক্ষা করা হয়। যদিও আমার ক্ষেত্রে, আমি স্পষ্টভাবে সাব-শেল - সেট করছি , এবং আশা করছি যে সাবশেলটি ব্যর্থতার পরে প্রস্থান করবে।
সাব

আমি একমত নই দ্বিতীয় পোস্টে (আমি অ্যাঙ্করদের কাজ করতে পারি না) বলেছে " পসিক্স বর্ণনাটি পরিষ্কার যে পার্শ্ববর্তী প্রসঙ্গটি সেট-এ সাব-শেল উপেক্ষা করা হবে কিনা তা প্রভাবিত করে " "- সাব-শেলটি অ্যান্ড-ওআর তালিকায় রয়েছে।
অ্যারন ডি মারাকো

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

তুমি ঠিক বলছো; আমি নিশ্চিত না যে আমি কীভাবে সেগুলি ভুল করে ফেলেছি। ধন্যবাদ।
ম্যাড সায়েন্টিস্ট

1
আমি জানতে পেরে আনন্দিত যে এই আচরণটি আমি আমার চুল ছিঁড়ে পসিক্স স্পেকের দিকে ফিরছি। তাহলে চারপাশে কী কাজ ?! ifএবং ||এবং &&সংক্রামক কি? এটি অযৌক্তিক
স্টিভেন লু

7

প্রকৃতপক্ষে, set -eসাবস্কেলগুলির অভ্যন্তরে কোনও প্রভাব নেই যদি আপনি ||তাদের পরে অপারেটর ব্যবহার করেন; উদাহরণস্বরূপ, এটি কাজ করবে না:

#!/bin/sh

# prints:
#
# --> outer
# --> inner
# ./so_1.sh: line 16: some_failed_command: command not found
# <-- inner
# <-- outer

set -e

outer() {
  echo '--> outer'
  (inner) || {
    exit_code=$?
    echo '--> cleanup'
    return $exit_code
  }
  echo '<-- outer'
}

inner() {
  set -e
  echo '--> inner'
  some_failed_command
  echo '<-- inner'
}

outer

তার উত্তরে অ্যারোন ডি ম্যারাস্কো কেন এটি এইভাবে আচরণ করে তা ব্যাখ্যা করার দুর্দান্ত কাজ করে।

এটি ঠিক করার জন্য এখানে একটি ছোট কৌশলটি ব্যবহার করা যেতে পারে: পটভূমিতে অভ্যন্তরীণ কমান্ডটি চালান, এবং তারপরে অবিলম্বে এটির জন্য অপেক্ষা করুন। waitBuiltin ভেতরের কমান্ডের প্রস্থান কোড ফিরে আসবে, এবং এখন আপনি ব্যবহার করছেন ||পরে wait, না ভেতরের ফাংশন, তাই set -eআধুনিক ভিতরে সঠিকভাবে কাজ করে:

#!/bin/sh

# prints:
#
# --> outer
# --> inner
# ./so_2.sh: line 27: some_failed_command: command not found
# --> cleanup

set -e

outer() {
  echo '--> outer'
  inner &
  wait $! || {
    exit_code=$?
    echo '--> cleanup'
    return $exit_code
  }
  echo '<-- outer'
}

inner() {
  set -e
  echo '--> inner'
  some_failed_command
  echo '<-- inner'
}

outer

এখানে জেনেরিক ফাংশন যা এই ধারণার উপর ভিত্তি করে। আপনি localকীওয়ার্ডগুলি সরিয়ে ফেললে সমস্ত পসিক্স-সামঞ্জস্যপূর্ণ শেলগুলিতে কাজ করা উচিত , অর্থাত্ সমস্তগুলি local x=yকেবলমাত্র দিয়ে প্রতিস্থাপন করুন x=y:

# [CLEANUP=cleanup_cmd] run cmd [args...]
#
# `cmd` and `args...` A command to run and its arguments.
#
# `cleanup_cmd` A command that is called after cmd has exited,
# and gets passed the same arguments as cmd. Additionally, the
# following environment variables are available to that command:
#
# - `RUN_CMD` contains the `cmd` that was passed to `run`;
# - `RUN_EXIT_CODE` contains the exit code of the command.
#
# If `cleanup_cmd` is set, `run` will return the exit code of that
# command. Otherwise, it will return the exit code of `cmd`.
#
run() {
  local cmd="$1"; shift
  local exit_code=0

  local e_was_set=1; if ! is_shell_attribute_set e; then
    set -e
    e_was_set=0
  fi

  "$cmd" "$@" &

  wait $! || {
    exit_code=$?
  }

  if [ "$e_was_set" = 0 ] && is_shell_attribute_set e; then
    set +e
  fi

  if [ -n "$CLEANUP" ]; then
    RUN_CMD="$cmd" RUN_EXIT_CODE="$exit_code" "$CLEANUP" "$@"
    return $?
  fi

  return $exit_code
}


is_shell_attribute_set() { # attribute, like "x"
  case "$-" in
    *"$1"*) return 0 ;;
    *)    return 1 ;;
  esac
}

ব্যবহারের উদাহরণ:

#!/bin/sh
set -e

# Source the file with the definition of `run` (previous code snippet).
# Alternatively, you may paste that code directly here and comment the next line.
. ./utils.sh


main() {
  echo "--> main: $@"
  CLEANUP=cleanup run inner "$@"
  echo "<-- main"
}


inner() {
  echo "--> inner: $@"
  sleep 0.5; if [ "$1" = 'fail' ]; then
    oh_my_god_look_at_this
  fi
  echo "<-- inner"
}


cleanup() {
  echo "--> cleanup: $@"
  echo "    RUN_CMD = '$RUN_CMD'"
  echo "    RUN_EXIT_CODE = $RUN_EXIT_CODE"
  sleep 0.3
  echo '<-- cleanup'
  return $RUN_EXIT_CODE
}

main "$@"

উদাহরণ চলছে:

$ ./so_3 fail; echo "exit code: $?"

--> main: fail
--> inner: fail
./so_3: line 15: oh_my_god_look_at_this: command not found
--> cleanup: fail
    RUN_CMD = 'inner'
    RUN_EXIT_CODE = 127
<-- cleanup
exit code: 127

$ ./so_3 pass; echo "exit code: $?"

--> main: pass
--> inner: pass
<-- inner
--> cleanup: pass
    RUN_CMD = 'inner'
    RUN_EXIT_CODE = 0
<-- cleanup
<-- main
exit code: 0

এই পদ্ধতিটি ব্যবহার করার সময় আপনার কেবলমাত্র সচেতন হওয়া দরকার তা হ'ল শেল ভেরিয়েবলের সমস্ত পরিবর্তনগুলি আপনি যে কমান্ডটি runদিয়েছিলেন তা কলিং ফাংশনে প্রচার করবে না, কারণ কমান্ডটি একটি সাবসিলে চলে।


2

বেশ কয়েকটি শেল সেভাবে আচরণ করায় আমি এটিকে ত্রুটিযুক্ত করব না। ;-)

আমি অফার আরও মজা আছে:

start cmd:> ( eval 'set -e'; false; echo passed; ) || echo failed
passed

start cmd:> ( eval 'set -e; false'; echo passed; ) || echo failed
failed

start cmd:> ( eval 'set -e; false; echo passed;' ) || echo failed
failed

আমি ম্যান বাশের কাছ থেকে উদ্ধৃতি দিতে পারি (৪.২.২৪):

শেলটি প্রস্থান করে না যদি কমান্ডটি ব্যর্থ হয় [...] কোনও && বা || তে সম্পাদিত কোনও আদেশের অংশ হয় চূড়ান্ত && বা || অনুসরণ করে কমান্ড বাদে তালিকান [...]

বেশ কয়েকটি আদেশের বিপরীতে সম্ভবত উপেক্ষা করা যায় || প্রসঙ্গ।


ভাল, যদি সমস্ত শেলগুলি সেভাবে আচরণ করে তবে এটি সংজ্ঞা অনুসারে কোনও বাগ নয় ... এটি স্ট্যান্ডার্ড আচরণ :-)। আমরা আচরণটি অ-স্বজ্ঞাত হিসাবে বিলাপ করতে পারি তবে ... এভাল সহ কৌশলটি খুব আকর্ষণীয়, এটি অবশ্যই নিশ্চিত।
ম্যাড সায়েন্টিস্ট

আপনি কোন শেল ব্যবহার করবেন? evalকৌতুক আমার জন্য কাজ করে না। আমি ব্যাশ, পোস্টিক্স মোডে ব্যাশ এবং ড্যাশ চেষ্টা করেছি।
ডুনাটোটাটোস

@ দুনাটাটাটোস, যেমন হউক বলেছেন, এটি ছিল বশ ৪৪.২। এটি bash4.3 এ "স্থির" হয়েছিল। পিডিএক্স-ভিত্তিক শেলগুলির একই "সমস্যা" থাকবে। এবং কয়েকটি শেলের বিভিন্ন সংস্করণে বিভিন্ন ধরণের "ইস্যু" রয়েছে set -eset -eনকশা দ্বারা ভাঙ্গা হয়। আমি এটিকে নিয়ন্ত্রণ স্ট্রাকচার, সাবহেল বা কমান্ড বিকল্প ছাড়াই শেল স্ক্রিপ্টগুলির সর্বাধিক ব্যতীত অন্য কোনও কিছুর জন্য ব্যবহার করব না।
স্টাফেন চেজেলাস

1

শীর্ষস্থানীয় যখন workaround set -e

আমি এই প্রশ্নে এসেছি কারণ আমি set -eত্রুটি সনাক্তকরণ পদ্ধতি হিসাবে ব্যবহার করছিলাম :

/usr/bin/env bash
set -e
do_stuff
( take_best_sub_action_1; take_best_sub_action_2 ) || do_worse_fallback
do_more_stuff

এবং ছাড়া ||, স্ক্রিপ্টটি চলমান থামবে এবং কখনই পৌঁছবে না do_more_stuff

যেহেতু কোনও পরিষ্কার সমাধান নেই বলে মনে হচ্ছে, আমি মনে করি আমি set +eআমার স্ক্রিপ্টগুলিতে একটি সাধারণ কাজ করব:

/usr/bin/env bash
set -e
do_stuff
set +e
( take_best_sub_action_1; take_best_sub_action_2 )
exit_status=$?
set -e
if [ "$exit_status" -ne 0 ]; then
  do_worse_fallback
fi
do_more_stuff
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.