পাথ স্বতন্ত্র শেবাংগুলি


20

আমার কাছে একটি স্ক্রিপ্ট রয়েছে যা আমি দুটি মেশিনে চালাতে সক্ষম হতে চাই। এই দুটি মেশিন একই গিট সংগ্রহস্থল থেকে স্ক্রিপ্টের অনুলিপি পেতে পারে। স্ক্রিপ্টটি ডান ইন্টারপ্রেটার (উদাহরণস্বরূপ zsh) দিয়ে চালানো দরকার ।

দুর্ভাগ্যক্রমে, উভয়ই env এবং zshস্থানীয় এবং দূরবর্তী মেশিনে বিভিন্ন স্থানে বাস:

রিমোট মেশিন

$ which env
/bin/env

$ which zsh
/some/long/path/to/the/right/zsh

লোকাল মেশিন

$ which env
/usr/bin/env

$which zsh
/usr/local/bin/zsh

আমি কীভাবে শেবাং সেট আপ করতে পারি যাতে স্ক্রিপ্টটি /path/to/script.shসর্বদা Zshউপলব্ধ হিসাবে ব্যবহার করে PATH?


8
আপনি কি নিশ্চিত envযে / বিন এবং / ইউএসআর / বিন উভয়ই নেই? which -a envনিশ্চিত করার চেষ্টা করুন।
মাধ্যাকর্ষণ

উত্তর:


22

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

zshউদাহরণস্বরূপ, এর একটি অনন্য বৈশিষ্ট্য হল $ZSH_VERSIONভেরিয়েবলের উপস্থিতি :

#!/bin/sh -

[ -z "$ZSH_VERSION" ] && exec zsh - "$0" ${1+"$@"}

# zsh-specific stuff following here
echo "$ZSH_VERSION"

এই সাধারণ ক্ষেত্রে, স্ক্রিপ্টটি প্রথমে সম্পাদিত হয় /bin/sh(80-এর দশকের পরে ইউনিক্স-মতো সমস্ত সিস্টেম বোঝে #!এবং একটি থাকে /bin/sh, হয় বোর্ন বা পসিক্স তবে আমাদের বাক্য গঠন উভয়ের সাথে সামঞ্জস্যপূর্ণ)। যদি $ZSH_VERSIONহয় না সেট, স্ক্রিপ্ট exec'নিজেই s থেকে zsh। যদি $ZSH_VERSIONসেট করা থাকে (রেস। স্ক্রিপ্টটি ইতিমধ্যে চলছে zsh), পরীক্ষাটি সহজভাবে বাদ দেওয়া হয়। Voila।

zshএকেবারেই না থাকলে এটি ব্যর্থ হয় $PATH

সম্পাদনা: তা নিশ্চিত করতে, আপনি শুধুমাত্র execএকটি zshস্বাভাবিক স্থানে, আপনি ভালো কিছু ব্যবহার করতে পারে

for sh in /bin/zsh \
          /usr/bin/zsh \
          /usr/local/bin/zsh; do
    [ -x "$sh" ] && exec "$sh" - "$0" ${1+"$@"}
done

এটি আপনাকে দুর্ঘটনাক্রমে এমন কোনও execকিছু তৈরি করা থেকে বাঁচাতে পারে $PATHযা zshআপনি প্রত্যাশা করেন না।


আমি কমনীয়তা এই upvoted কিন্তু এটি করে নীতিগতভাবে, নিরাপত্তা / সামঞ্জস্য বিষয় আছে, যদি প্রথম zshমধ্যে $PATHএক আশা নয়।
রায়ান রেইচ

এটিকে সম্বোধনের চেষ্টা করেছি। প্রশ্নটি হল, zshমানক অবস্থানগুলিতে বাইনারি আসলেই একটি কিনা আপনি সর্বদা নিশ্চিত থাকতে পারেন কিনা zsh
আন্দ্রেস উইস

আপনি गतिशीलভাবে! ঠুং লাইনের দিকে যেতে পারেন। zshএটি কোথায় রয়েছে তা আপনি নিজেই জিজ্ঞাসা করতে পারেন zsh -c 'whence zsh'। আরও সহজভাবে আপনি ঠিক করতে পারেন command -v zsh। কীভাবে গতিশীলভাবে পথ চলতে হবে তার জন্য আমার উত্তর দেখুন #!bang
মাইকসার্ভ

1
zshবাইনারি থেকে বাইনারিটির $PATHপথ পেতে কল করা zshসমস্যার যথেষ্ট সমাধান করবে না @ রায়ানআরাইচ দেখিয়েছেন, তাই না? :)
Andreas Wiese

না যদি আপনি zshনিজেই মৃত্যুদণ্ড কার্যকর করেন , না, আমি অনুমান করি না। তবে যদি আপনি আপনার হ্যাশ ব্যাংগুলিতে ফলাফলটি স্ট্রিং এম্বেড করেন এবং তারপরে আপনার নিজের স্ক্রিপ্টটি সম্পাদন করেন তবে আপনি কী পেয়েছেন তা অন্তত জানেন know তবুও, এটি লুপিংয়ের চেয়ে আরও সহজ পরীক্ষা করার চেষ্টা করবে।
মাইকসার্ভ

7

কয়েক বছর ধরে আমি সিস্টেমে ব্যাশের বিভিন্ন অবস্থান নিয়ে কাজ করার জন্য অনুরূপ কিছু ব্যবহার করেছি যা চালানোর জন্য আমার স্ক্রিপ্টগুলির প্রয়োজন needed

ব্যাশ / Zsh / ইত্যাদি।

#!/bin/sh

# Determines which OS and then reruns this script with approp. shell interp.
LIN_BASH="/bin/sh";
SOL_BASH="/packages/utilities/bin/sun5/bash";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  $SOL_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
elif [ $OS_TYPE = "Linux" ]; then
  $LIN_BASH -c "`sed -n '/\#\#\# BEGIN/,$p' $0`" $0 $*;
else
  echo "UNKNOWN OS_TYPE, $OS_TYPE";
  exit 1;
fi
exit 0;

### BEGIN

...script goes here...

উপরোক্ত বিভিন্ন দোভাষী জন্য সহজেই মানিয়ে নেওয়া যেতে পারে। মূল অংশটি হ'ল এই স্ক্রিপ্টটি প্রাথমিকভাবে বোর্ন শেল হিসাবে চলমান। এরপরে এটি পুনরাবৃত্তভাবে দ্বিতীয় বার নিজেকে কল করে তবে মন্তব্যটির উপরে থাকা সমস্ত কিছুকে বিশ্লেষণ করে পার্স ### BEGINকরে sed

পার্ল

পার্লের জন্য এখানে একটি অনুরূপ কৌশল:

#!/bin/sh

LIN_PERL="/usr/bin/perl";
SOL_PERL="/packages/perl/bin/perl";

OS_TYPE=`uname -s`;

if [ $OS_TYPE = "SunOS" ]; then
  eval 'exec $SOL_PERL -x -S $0 ${1+"$@"}';
elif [ $OS_TYPE = "Linux" ]; then
  eval 'exec $LIN_PERL -x -S $0 ${1+"$@"}';
else
  echo "$OS_TYPE: UNSUPORRTED OS/PLATFORM";
  exit 0;
fi
exit 0;

#!perl

...perl script goes here...

এই পদ্ধতিটি পার্লের ক্ষমতাকে কাজে লাগায় যখন কোনও ফাইল চালানোর সময় দেওয়া হয় তা বলে দেয় যে ফাইলটি লাইনের আগে থাকা সমস্ত লাইনকে এড়িয়ে চলেছে #! perl


এখানে বেশ কয়েকটি সমস্যা রয়েছে: অনুপস্থিত উদ্ধৃতি, $*পরিবর্তে এর "$@"ব্যবহার, ইওল এর অকেজো ব্যবহার, প্রস্থান স্থিতি রিপোর্ট করা হয়নি (আপনি execপ্রথমটির জন্য ব্যবহার করেন নি ), অনুপস্থিত -/ --, ত্রুটি বার্তাগুলি স্টাডারে নেই, ত্রুটির অবস্থার জন্য 0 প্রস্থান স্থিতি , LIN_BASH এর জন্য / বিন / শ ব্যবহার করে অকেজো সেমিকোলন (প্রসাধনী) ব্যবহার করুন, এন-এনভি ভেরিয়েবলের জন্য সমস্ত বড় হাতের অক্ষর ব্যবহার করে। uname -sএর মতো uname(আনামিক ইউনিক্স নামের জন্য)। আপনি উল্লেখ করতে ভুলে গেছেন যে এড়িয়ে যাওয়াটি-x বিকল্পটি দ্বারা চালিত হয়েছে perl
স্টাফেন চেজেলাস

4

দ্রষ্টব্য: @ jw013 নীচের মন্তব্যে নিম্নলিখিত অসমর্থিত আপত্তি তোলে :

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

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

আমি কীভাবে শেবাং সেট আপ করতে পারি যাতে স্ক্রিপ্টটি /path/to/script.sh হিসাবে চালানো সর্বদা PATH এ উপলব্ধ Zsh ব্যবহার করে?

সন্তুষ্ট নন, @ jw013 কমপক্ষে দু'দফা ভুল বক্তব্য দিয়ে তাঁর এখনও অসমর্থিত যুক্তিটি প্রকাশ করে আপত্তি জানাতে থাকলেন :

আপনি একটি ফাইল ব্যবহার করেন, দুটি ফাইল নয়। [ man shরেফারেন্সড] প্যাকেজ একটি ফাইল অন্য ফাইল পরিবর্তন হয়েছে। আপনার নিজের মধ্যে ফাইল পরিবর্তন করতে হবে। এই দুটি ক্ষেত্রে পৃথক পার্থক্য রয়েছে। একটি ফাইল যা ইনপুট নেয় এবং আউটপুট উত্পাদন করে তা ঠিক। একটি এক্সিকিউটেবল ফাইল যা এটি চলার সাথে সাথে নিজেকে বদলে দেয় তা সাধারণত একটি খারাপ ধারণা। আপনি যে উদাহরণটি নির্দেশ করেছেন তা তা করে না।

প্রথম অবস্থানে:

কেবলমাত্র এক্সিকিউটেবল কোনো কোড এক্সিকিউটেবল শেল স্ক্রিপ্ট IS #!নিজেকে

(যদিও এমনকি #!নেই আনুষ্ঠানিকভাবে অনির্দিষ্ট )

{   cat >|./file 
    chmod +x ./file 
    ./file
} <<-\FILE
    #!/usr/bin/sh
    {   ${l=lsof -p} $$
        echo "$l \$$" | sh
    } | grep \
        "COMMAND\|^..*sh\| [0-9]*[wru] "
#END
FILE

##OUTPUT

COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
file    8900 mikeserv  txt    REG   0,33   774976  2148676 /usr/bin/bash
file    8900 mikeserv  mem    REG   0,30           2148676 /usr/bin/bash (path dev=0,33)
file    8900 mikeserv    0r   REG   0,35      108 15496912 /tmp/zshUTTARQ (deleted)
file    8900 mikeserv    1u   CHR  136,2      0t0        5 /dev/pts/2
file    8900 mikeserv    2u   CHR  136,2      0t0        5 /dev/pts/2
file    8900 mikeserv  255r   REG   0,33      108  2134129 /home/mikeserv/file
COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
sh      8906 mikeserv  txt    REG   0,33   774976  2148676 /usr/bin/bash
sh      8906 mikeserv  mem    REG   0,30           2148676 /usr/bin/bash (path dev=0,33)
sh      8906 mikeserv    0r  FIFO    0,8      0t0 15500515 pipe
sh      8906 mikeserv    1w  FIFO    0,8      0t0 15500514 pipe
sh      8906 mikeserv    2u   CHR  136,2      0t0        5 /dev/pts/2

{    sed -i \
         '1c#!/home/mikeserv/file' ./file 
     ./file 
     sh -c './file ; echo'
     grep '#!' ./file
}

##OUTPUT
zsh: too many levels of symbolic links: ./file
sh: ./file: /home/mikeserv/file: bad interpreter: Too many levels of symbolic links

#!/home/mikeserv/file

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

একটি শেল স্ক্রিপ্টের হ্যাশবাং অবশ্যই এটির উদ্দেশ্যে করা দোভাষীকে ইঙ্গিত করতে পারে বা অপ্রাসঙ্গিক হিসাবে ফেলে দেওয়া উচিত।

শেল এর টোকেনটি স্বীকৃতি / কার্যকারীকরণ আচরণ স্ট্যান্ডার্ডগুলি-সংজ্ঞায়িত করা হয়

শেল পার্স এবং তার ইনপুট ব্যাখ্যা দুটি মৌলিক মোড আছে: পারেন তার বর্তমান ইনপুট একটি সংজ্ঞা করা হয় <<here_documentবা এটি একটি সংজ্ঞা করা হয় { ( command |&&|| list ) ; } &- অন্য কথায়, শেল পারেন ব্যাখ্যা করে একটি টোকেন কমান্ড একবার এটা পড়েছেন এটি চালানো উচিত একটি বিভেদক হিসাবে কোনও ফাইল তৈরি করতে এবং অন্য কমান্ডের জন্য ফাইল বর্ণনাকারীতে এটি ম্যাপ করার নির্দেশাবলী হিসাবে বা এটাই.

শেলটি কার্যকর করতে আদেশগুলি ব্যাখ্যা করার সময় সংরক্ষিত শব্দের সংকলনে টোকেনগুলি সীমানা করে যখন প্রযোজ্য - - বা ক্লোজিং টোকেনের মতোই শেল খোলা টোকেন এটা যেমন একটি newline যেমন কমান্ড তালিকায় পড়তে চালিয়ে যেতে হবে যতক্ষণ না তালিকা পারেন টোকেন একটি ক্লোজিং দ্বারা সীমায়িত করা হয় encounters যখন })জন্য ({মৃত্যুদন্ড আগে।

শেল একটি সাধারণ কমান্ড এবং একটি যৌগিক কমান্ডের মধ্যে পার্থক্য করে যৌগ কমান্ড কমান্ড যে মৃত্যুদন্ড সামনে পড়া করা আবশ্যক সেট, কিন্তু শেল সঞ্চালন নেই $expansionতার উপাদান কোনো সহজ কমান্ড পর্যন্ত এটি একেলা প্রতিটি এক সঞ্চালন করে।

সুতরাং, নিম্নলিখিত উদাহরণে, ;semicolon সংরক্ষিত শব্দগুলি পৃথক সরল কমান্ডের সীমানা নির্ধারণ করে যেখানে \newlineঅরক্ষিত অক্ষর দুটি যৌগিক কমান্ডের মধ্যে সীমাবদ্ধ করে:

{   cat >|./file
    chmod +x ./file
    ./file
} <<-\FILE
        #!/usr/bin/sh
        echo "simple command ${sc=1}" ;\
                : > $0 ;\
                echo "simple command $((sc+2))" ;\
                sh -c "./file && echo hooray"
        sh -c "./file && echo hooray"
#END
FILE

##OUTPUT

simple command 1
simple command 3
hooray

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

এবং বিল্ট-ইনগুলি এবং কমান্ড-তালিকাগুলির কথা বলতে গেলে a function() { declaration ; }কেবল একটি সাধারণ কমান্ডের জন্য একটি যৌগিক কমান্ড নির্ধারণের একটি উপায়। শেলটি অবশ্যই $expansionsডিক্লেয়ারেশন স্টেটমেন্টে নিজেই কোনও সম্পাদন করতে পারে না <<redirections>- অন্তর্ভুক্ত করতে - তবে এর পরিবর্তে সংজ্ঞাটি একটি একক, আক্ষরিক স্ট্রিং হিসাবে সংরক্ষণ করতে হবে এবং ডাকা হলে এটি একটি বিশেষ শেল হিসাবে নির্মিত হবে built

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

একটি <<HERE-DOCUMENTহ'ল একটি অনলাইন ফাইল

পুনঃনির্দেশ অপারেটর <<এবং <<-উভয়ই কমান্ডের ইনপুটটিতে শেল ইনপুট ফাইলে থাকা লাইনের পুনঃনির্দেশের অনুমতি দেয়, যা এখানে নথি হিসাবে পরিচিত ।

এখানে নথি একটি কথাও আগামী পর শুরু হয় হিসাবে গণ্য হইবে \newlineএবং যতক্ষণ সেখানে মাত্র ধারণকারী একটি লাইন চলতে বিভেদক এবং \newline, কোন সঙ্গে [:blank:]মাঝে সে। তারপরে পরবর্তী এখানে-নথিটি শুরু হয়, যদি একটি থাকে। ফর্ম্যাটটি নিম্নরূপ:

[n]<<word
    here-document 
delimiter

... যেখানে al nচ্ছিক ফাইল বর্ণনাকারী নম্বর উপস্থাপন করে। যদি নম্বরটি বাদ দেওয়া হয় তবে এখানে-নথিটি স্ট্যান্ডার্ড ইনপুট (ফাইল বর্ণনাকারী 0) বোঝায় ।

for shell in dash zsh bash sh ; do sudo $shell -c '
        {   readlink /proc/self/fd/3
            cat <&3
        } 3<<-FILE
            $0

        FILE
' ; done

#OUTPUT

pipe:[16582351]
dash

/tmp/zshqs0lKX (deleted)
zsh

/tmp/sh-thd-955082504 (deleted)
bash

/tmp/sh-thd-955082612 (deleted)
sh

দেখেন তো? শেলের উপরের প্রতিটি শেলের জন্য একটি ফাইল তৈরি করে এটি একটি ফাইল বর্ণনাকারীতে মানচিত্র করে। ইন zsh, (ba)shশেল মধ্যে একজন নিয়মিত ফাইল তৈরি করে /tmp, আউটপুট ডাম্প, একটি বর্ণনাকারী তা মানচিত্র, তারপর মুছে ফেলে /tmpফাইল যাতে বর্ণনাকারী এর কার্নেল এর অনুলিপি যে সব থাকে। dashএই সমস্ত বাজে কথা এড়িয়ে চলে এবং |pipeপুনঃনির্দেশ লক্ষ্যকে <<লক্ষ্য করে একটি অনামী ফাইলের মধ্যে তার আউটপুট প্রসেসিংটিকে কেবল ড্রপ করে ।

এটি তোলে dash:

cmd <<HEREDOC
    $(cmd)
HEREDOC

কার্যত সমান bash:

cmd <(cmd)

যখন dashএর বাস্তবায়ন অন্তত POSIXly পোর্টেবল।

যা বিভিন্ন ফাইল তৈরি করে

নীচের উত্তরে আমি যখন করি তখন:

{    cat >|./file
     chmod +x ./file
     ./file
} <<\FILE
#!/usr/bin/sh
_fn() { printf '#!' ; command -v zsh ; cat 
} <<SCRIPT >$0
    [SCRIPT BODY]
SCRIPT    

_fn ; exec $0
FILE

নিম্নলিখিত ঘটে:

  1. আমি প্রথম catযাই হোক না কেন ফাইল শেল জন্য তৈরি বিষয়বস্তু FILEমধ্যে ./file, এটা এক্সিকিউটেবল করুন, তারপরে এটি চালানো।

  2. কার্নেলটি ব্যাখ্যা করে #!এবং নির্ধারিত /usr/bin/shএকটি <read ফাইল বর্ণনাকারীর সাথে কল করে ./file

  3. shযৌগিক কমান্ডটি শুরু _fn()এবং শেষ হওয়া মিশ্রণে একটি স্ট্রিং মানচিত্র করে SCRIPT

  4. যখন _fnবলা হয়, shপ্রথম তারপর একটি বর্ণনাকারী ফাইল সংজ্ঞায়িত মানচিত্র ব্যাখ্যা করতে হবে <<SCRIPT...SCRIPT আগে invoking _fnবিল্ট ইন ইউটিলিটি একটি বিশেষ হিসাবে কারণ SCRIPTহল _fnএর<input.

  5. স্ট্রিং আউটপুট দ্বারা printfএবং commandআউট লেখা হয় _fn'র মান-আউট >&1 - যা বর্তমান শেল এর থেকে আপনাকে পুনঃনির্দেশিত করা হয় ARGV0- বা $0

  6. catএর <&0 স্ট্যান্ডার্ড-ইনপুট ফাইল-বর্ণনাকারীকে সংযুক্ত করে - SCRIPT- >কাটা বর্তমান শেলের ARGV0আর্গুমেন্টের উপর, বা $0

  7. এর ইতিমধ্যে পঠিত বর্তমান যৌগিক কমান্ডটি সম্পূর্ণ করা , sh execএক্সিকিউটেবল - এবং সদ্য পুনর্লিখন - $0যুক্তি।

সেই সময় থেকে ./fileডেকে আনা হয় যতক্ষণ না এর অন্তর্ভুক্ত নির্দেশাবলী নির্দিষ্ট করে যে এটি execআবার ডি হওয়া উচিত , shএটি একবারে একটি একক যৌগ কমান্ডে পড়ে যখন এটি কার্যকর করে, যখন ./fileনিজেই নতুন বিষয়বস্তু আনন্দের সাথে গ্রহণ না করে কিছুই করে না । প্রকৃতপক্ষে কর্মরত ফাইলগুলি হ'ল/usr/bin/sh, /usr/bin/cat, /tmp/sh-something-or-another.

ধন্যবাদ, সব পরে

সুতরাং যখন @ jw013 নির্দিষ্ট করে:

একটি ফাইল যা ইনপুট নেয় এবং আউটপুট উত্পাদন করে তা ঠিক ...

... এই উত্তরের ভ্রান্ত সমালোচনার মধ্যেও তিনি অজ্ঞাতসারে এখানে ব্যবহৃত একমাত্র পদ্ধতিটি ক্ষমা করছেন, যা মূলত ন্যায়বিচারের জন্য কাজ করে:

cat <new_file >old_file

উত্তর

এখানে সমস্ত উত্তর ভাল তবে এগুলির কোনওোটাই পুরোপুরি সঠিক নয়। প্রত্যেকে দাবি করছে যে আপনি গতিশীল এবং স্থায়ীভাবে আপনার পথে যেতে পারবেন না #!bang। এখানে একটি পাথ স্বতন্ত্র শেবাং স্থাপনের একটি বিক্ষোভ রয়েছে:

ডেমো

{   cat >|./file
    chmod +x ./file
    ./file
} <<\FILE 
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
        ${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me ; exec $0
FILE

আউটপুট

        $0    : ./file
        lines : 13
        !bang : #!/usr/bin/sh
        shell : /usr/bin/sh

1 >     #!/usr/bin/sh
2 >     _rewrite_me() { printf '#!' ; command -v zsh
...
12 >    SCRIPT
13 >    _rewrite_me ; out=$0 _rewrite_me ; exec $0

        $0    : /home/mikeserv/file
        lines : 8
        !bang : #!/usr/bin/zsh
        shell : /usr/bin/zsh

1 >     #!/usr/bin/zsh
2 >             printf "
...
7 >             sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
8 >                     sed -e 'N;s/\n/ >\t/' -e 4a\\...

দেখেন তো? আমরা কেবল স্ক্রিপ্টটিকে নিজেই ওভাররাইট করি। এবং এটি gitসিঙ্কের পরে কেবল একবারই ঘটে । সেই দিক থেকে এটি #! ব্যাং লাইনে সঠিক পথ পেয়েছে।

এখন প্রায় সেখানে প্রায় পুরোপুরি ঝাঁকুনি আছে। নিরাপদে এটি করতে আপনার প্রয়োজন:

  1. শীর্ষে সংজ্ঞায়িত একটি ফাংশন এবং নীচে ডাকা হয় যা লেখাটি করে। এইভাবে আমরা মেমরিতে আমাদের প্রয়োজনীয় সমস্ত জিনিস সঞ্চয় করি এবং এটি লেখা শুরু করার আগে পুরো ফাইলটি অবশ্যই পঠিত রয়েছে তা নিশ্চিত করি।

  2. পথটি কী হওয়া উচিত তা নির্ধারণের কিছু উপায়। command -vএটির জন্য বেশ ভাল।

  3. বংশগতরা সত্যই সহায়তা করে কারণ তারা প্রকৃত ফাইল। এর মধ্যে তারা আপনার স্ক্রিপ্ট সংরক্ষণ করবে। আপনি স্ট্রিং ব্যবহার করতে পারেন তবে ...

  4. আপনাকে নিশ্চিত করতে হবে যে শেলটি যে কমান্ডটিতে পড়েছে তা একই কমান্ড তালিকায় আপনার স্ক্রিপ্টটিকে যেটি কার্যকর করে তার ওভাররাইট করে।

দেখুন:

{   cat >|./file
    chmod +x ./file
    ./file
} <<\FILE 
#!/usr/bin/sh
_rewrite_me() { printf '#!' ; command -v zsh
        ${out+cat} ; ${out+:} . /dev/fd/0 >&2
} <<\SCRIPT >|${out-/dev/null}
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...
SCRIPT
_rewrite_me ; out=$0 _rewrite_me
exec $0
FILE

লক্ষ্য করুন আমি execকমান্ডটি কেবল একটি লাইনে সরিয়েছি। এখন:

#OUTPUT
        $0    : ./file
        lines : 14
        !bang : #!/usr/bin/sh
        shell : /usr/bin/sh

1 >     #!/usr/bin/sh
2 >     _rewrite_me() { printf '#!' ; command -v zsh
...
13 >    _rewrite_me ; out=$0 _rewrite_me
14 >    exec $0

আমি আউটপুটটির দ্বিতীয়ার্ধটি পাই না কারণ স্ক্রিপ্টটি পরবর্তী কমান্ডে পড়তে পারে না। তবুও, কারণ একমাত্র আদেশটি অনুপস্থিত ছিল শেষ:

cat ./file

#!/usr/bin/zsh
        printf "
        \$0    :\t$0
        lines :\t$((c=$(wc -l <$0)))
        !bang :\t$(sed 1q "$0")
        shell :\t"$(printf `ps -o args= -p $$`)\\n\\n
        sed -n "1,2{=;p};$((c-1)),\${=;p}" "$0" |
                sed -e 'N;s/\n/ >\t/' -e 4a\\...

স্ক্রিপ্টটি যেমনটি হওয়া উচিত ছিল তেমনটি এসেছে - বেশিরভাগ কারণ এটি সমস্তই হেরডোকের মধ্যে ছিল - তবে আপনি যদি এটি ঠিকভাবে পরিকল্পনা না করেন তবে আপনি আপনার ফাইল স্ট্রিমটি কেটে ফেলতে পারবেন, যা আমার উপরে ঘটেছে।


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

@ jw013 স্পষ্টতই স্ক্রিপ্টটি ইনস্টল বা আপডেট করার চেষ্টা করা ব্যক্তির স্ক্রিপ্ট ইনস্টল বা আপডেট করার অনুমতি না থাকলে এক্সিকিউটেবল স্ক্রিপ্ট ইনস্টল বা আপডেট করার জন্য আমার পদ্ধতির কাজ হবে না প্রকৃতপক্ষে, এটি বিশেষত যা এই উত্তরটি এখানে অন্য প্রতিটি উত্তরের চেয়ে ভাল করে তোলে - এটি যথাযথ #! ঠুং লাইন সরবরাহ করতে পারে প্রয়োজন হিসাবে এবং ইনস্টলেশনটির সময় কেবল প্রথম অনুরোধে এটি করার জন্য কোনও বিশেষ অনুমতি প্রয়োজন এবং আবারও, আমি এটির জন্য আপনার শব্দটি কেবল গ্রহণ করব না যে স্ব-সংশোধনকারী কোডটি খারাপ অভ্যাস - দয়া করে man commandএকটি বিপরীত মতামত দেখুন।
মাইকজার্ভ

man commandএকটি বিরোধী মতামত জন্য দয়া করে দেখুন - একটি খুঁজে না। আপনি যে নির্দিষ্ট বিভাগ / অনুচ্ছেদে কথা বলছেন তা আমাকে গাইড করতে পারেন?
jw013

@ jw013 - আমার ভুল, এটি এতে রয়েছে man sh- 'কমান্ড-ভি' অনুসন্ধান করুন। আমি জানতাম যে এটি manঅন্য পৃষ্ঠায় আমি যে পৃষ্ঠাগুলিতে দেখছিলাম তার একটিতে ।
মাইক্রজারভ

আমি অনুমান এই হয় command -vউদাহরণস্বরূপ আপনার কাছ থেকে সম্পর্কে কথা বলা হয়েছে man sh। এটি একটি সাধারণ-বর্ণিত ইনস্টলার স্ক্রিপ্ট, এবং স্ব-পরিবর্তনকারী কোনও নয় । এমনকি স্ব-অন্তর্ভুক্ত ইনস্টলারগুলিতে কেবল প্রাক-পরিবর্তন ইনপুট থাকে এবং অন্য কোথাও তাদের পরিবর্তনগুলি আউটপুট করে। আপনার প্রস্তাব অনুসারে তারা নিজেরাই পুনরায় লেখেন না rite
jw013

1

এখানে স্ব-পরিমার্জনকারী স্ক্রিপ্ট থাকার একটি উপায় যা এর শেবাং স্থির করে। এই কোডটি আপনার আসল স্ক্রিপ্টে প্রম্পেন্ড করা উচিত।

#!/bin/sh
# unpatched

PATH=`PATH=/bin:/usr/bin:$PATH getconf PATH`
if [ "`awk 'NR==2 {print $2;exit;}' $0`" = unpatched ]; then
  [ -z "`PATH=\`getconf PATH\`:/usr/local/bin:/some/long/path/to/the/right:$PATH command -v zsh`" ] && { echo "zsh not found"; exit 1; }
  cp -- "$0" "$0.org" || exit 1
  mv -- "$0" "$0.old" || exit 1
  (
    echo "#!`PATH=\`getconf PATH\`:$PATH command -v zsh`" 
    sed -n '/^##/,$p' $0.old
  ) > $0 || exit
  chmod +x $0
  rm $0.old
  sync
  exit
fi
## Original script starts here

কিছু মন্তব্য:

  • স্ক্রিপ্টটি যেখানে রয়েছে সেই ডিরেক্টরিতে ফাইলগুলি তৈরি এবং মুছে ফেলার অধিকার রয়েছে এমন একজনের দ্বারা এটি একবার চালানো উচিত।

  • এটি কেবলমাত্র লেগ্যাসি বোর্ন শেল সিনট্যাক্স ব্যবহার করে কারণ জনপ্রিয় বিশ্বাস থাকা সত্ত্বেও, পসিএক্স শেল হিসাবে /bin/shগ্যারান্টিযুক্ত নয় এমনকি পোসিক্স কমপ্লায়েন্ট ওএসসও রয়েছে।

  • এটি "ফোনি" জেডএসএইচ বাছাই করা এড়ানোর জন্য সম্ভাব্য জেএসএস লোকেশনগুলির একটি তালিকা অনুসরণ করে একটি পসিক্স অনুগামী হিসাবে PATH সেট করে।

  • যদি কোনও কারণে স্ব-সংশোধনকারী স্ক্রিপ্ট অপ্রয়োজনীয় হয় তবে এটির পরিবর্তে দুটি স্ক্রিপ্ট বিতরণ করা তুচ্ছ হবে, প্রথমটি হ'ল আপনি প্যাচ করতে চান এবং দ্বিতীয়টি, আমি প্রস্তাবিত একটি পূর্বের প্রক্রিয়া করার জন্য সামান্য পরিবর্তিত হয়েছিল।


/bin/shবিন্দু ভালো - কিন্তু যে ক্ষেত্রে আপনি একটি premodified প্রয়োজন#! এ সব? আর নয় awkঠিক যেমন হতে পারে অপ্রকৃত যেমন zshহয়?
মাইকজার্ভ

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

ইন্দ্রিয় তোলে। আমি এটি উত্সাহিত করেছি কারণ এটি কাজ করে, এটি বইটিতে লেগে যায় এবং এটি সম্ভাব্য শেল এনভায়রনমেন্ট / ফাইল হ্যান্ডলিংয়ের বিশেষ উপলব্ধি প্রদর্শন করে - বিশেষত আপনি যে ব্যাকআপ ফাইলগুলি ব্যবহার করেন যা জিএনইউর sed -iযেভাবেই হয়। আমি ব্যক্তিগতভাবে মনে করি যে $PATHসমস্যাটি অন্য উত্তরের মন্তব্যে উল্লিখিত হয়েছে এবং আপনি যা নিরাপদে সম্বোধন করেছেন আমি এখানে কয়েকটি লাইনে চিত্রিত করতে পারছি কেবলমাত্র এবং সুস্পষ্টভাবে নির্ভরতা এবং / অথবা কঠোর এবং স্পষ্ট পরীক্ষার সংজ্ঞা দিয়ে আরও ভালভাবে পরিচালনা করা - উদাহরণস্বরূপ, এখন getconfহতে পারে নোংরা, তবে সম্ভাবনাগুলি শূন্যের কাছাকাছি, তারা যেমন ছিল zshawk.
তেমনই

@ মিমকিজার, স্ক্রিপ্ট পরিবর্তন করে একটি বোগাস গেটকনফ কল করার ঝুঁকি কমাতে।
jlliagre

$(getconf PATH) বোর্ন নয়। cp $0 $0.oldzsh সিনট্যাক্স। বোর্নের সমতুল্য cp "$0" "$0.old"আপনি cp -- "$0" "$0.old"
চাইলেও
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.