كيفية الهروب ونقلت واحدة داخل واحدة نقلت سلاسل

StackOverflow https://stackoverflow.com/questions/1250079

  •  12-09-2019
  •  | 
  •  

سؤال

دعونا نقول لديك باش alias مثل:

alias rxvt='urxvt'

الذي يعمل بشكل جيد.

ومع ذلك:

alias rxvt='urxvt -fg '#111111' -bg '#111111''

لن تعمل ولا:

alias rxvt='urxvt -fg \'#111111\' -bg \'#111111\''

كيف كنت في نهاية المطاف مطابقة فتح وإغلاق ونقلت داخل السلسلة مرة واحدة كنت قد نجا من الاقتباس ؟

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''

يبدو صعب المراس على الرغم من أنها تمثل نفس السلسلة إذا كنت يسمح لك لسلسلة لهم مثل ذلك.

هل كانت مفيدة؟

المحلول

إذا كنت ترغب حقا في استخدام علامات اقتباس واحدة في الطبقة الخارجية، تذكر أنه يمكنك الغراء كلا النوعين من الاقتباس. مثال:

 alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
 #                     ^^^^^       ^^^^^     ^^^^^       ^^^^
 #                     12345       12345     12345       1234

شرح حول كيفية ذلك '"'"' يتم تفسيره فقط ':

  1. ' نهاية الاقتباس الأول الذي يستخدم علامات اقتباس واحدة.
  2. " ابدأ الاقتباس الثاني، باستخدام علامات اقتباس مزدوجة.
  3. ' حرف مقتبس.
  4. " نهاية الاقتباس الثاني، باستخدام اقتباسات مزدوجة.
  5. ' ابدأ اقتباس ثالث، باستخدام علامات اقتباس واحدة.

إذا لم تقم بوضع أي Whitespaces بين (1) و (2)، أو بين (4) و (5)، فسوف تفسر القذيفة هذه السلسلة ككلمة واحدة طويلة.

نصائح أخرى

أنا دائما استبدال كل اقتباس واحد مضمن مع التسلسل: '\'' (هذا هو: اقتباس Backslash Quote Quote) الذي يغلق السلسلة، إلحاق النزول اقتباس واحد وإعادة فتح السلسلة.


أنا في كثير من الأحيان سوط وظيفة "نقراوية" في البرامج النصية بيرل الخاصة بي للقيام بذلك بالنسبة لي. الخطوات ستكون:

s/'/'\\''/g    # Handle each embedded quote
$_ = qq['$_']; # Surround result with single quotes.

هذا يعتني كثيرا بجميع الحالات.

الحياة تصبح أكثر متعة عندما تقدم eval في البرامج النصية شل الخاص بك. عليك بشكل أساسي إعادة قياسية كل شيء مرة أخرى!

على سبيل المثال، قم بإنشاء برنامج نصي بيرل يسمى نقراضا يحتوي على العبارات أعلاه:

#!/usr/bin/perl -pl
s/'/'\\''/g;
$_ = qq['$_'];

ثم استخدمه لإنشاء سلسلة مقتبسة بشكل صحيح:

$ quotify
urxvt -fg '#111111' -bg '#111111'

نتيجة:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

والتي يمكن بعد ذلك أن تكون نسخة / لصقها في الأمر الاسم المستعار:

alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

(إذا كنت بحاجة إلى إدراج الأمر في Eval، قم بتشغيل النطاقي مرة أخرى:

 $ quotify
 alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

نتيجة:

'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

والتي يمكن أن تكون نسخ / لصقها في eval:

eval 'alias rxvt='\''urxvt -fg '\''\'\'''\''#111111'\''\'\'''\'' -bg '\''\'\'''\''#111111'\''\'\'''\'''\'''

منذ باش 2.04 بناء الجملة $'string' (بدلا من مجرد 'string'; ؛ تحذير: لا تخلط مع $('string')) هي آلية اقتباس أخرى تسمح ANSI C - مثل تسلسل الهروب والقيام بالتوسع إلى إصدار واحد مقتبس.

مثال بسيط:

  $> echo $'aa\'bb'
  aa'bb

  $> alias myvar=$'aa\'bb'
  $> alias myvar
  alias myvar='aa'\''bb'

في حالتك:

$> alias rxvt=$'urxvt -fg \'#111111\' -bg \'#111111\''
$> alias rxvt
alias rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

تسلسل الهروب المشترك يعمل كما هو متوقع:

\'     single quote
\"     double quote
\\     backslash
\n     new line
\t     horizontal tab
\r     carriage return

أدناه هي نسخة + مستندات متعلقة لصق man bash (الإصدار 4.4):

يتم التعامل مع كلمات النموذج $ "سلسلة" خاصة. توسعت الكلمة إلى السلسلة، مع استبدال الأحرف الخلفية التي هربها على النحو المحدد بواسطة قياسي ANSI C. تراجع تسلسل الهروب الخلفية، إن وجدت، كما يلي:

    \a     alert (bell)
    \b     backspace
    \e
    \E     an escape character
    \f     form feed
    \n     new line
    \r     carriage return
    \t     horizontal tab
    \v     vertical tab
    \\     backslash
    \'     single quote
    \"     double quote
    \?     question mark
    \nnn   the eight-bit character whose value is the octal 
           value nnn (one to three digits)
    \xHH   the eight-bit character whose value is the hexadecimal
           value HH (one or two hex digits)
    \uHHHH the Unicode (ISO/IEC 10646) character whose value is 
           the hexadecimal value HHHH (one to four hex digits)
    \UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value 
               is the hexadecimal value HHHHHHHH (one to eight 
               hex digits)
    \cx    a control-x character

النتيجة الموسعة هي واحدة مقتبسة، كما لو كانت علامة الدولار لم تكن موجودة.


يرى يقتبس والهروب: ansi c مثل السلاسل على bash-hackers.org Wiki لمزيد من التفاصيل. لاحظ أيضا ذلك "تغييرات باش" ملف (نظرة عامة هنا) يذكر الكثير للتغييرات وإصلاح الأخطاء المتعلقة $'string' نقلا عن آلية.

وفقا ل UNIX.STACKEXCHANGE.COM. كيفية استخدام حرف خاص كواحد طبيعي؟ يجب أن تعمل (مع بعض الاختلافات) في باش، ZSH، MKSH، KSH93 و FreeBSD و Busybox Sh.

أنا لا أرى الدخول على مدونته (رابط الثابتة والمتنقلة؟) ولكن وفقا ل دليل إشارة جنو:

يحافظ أرفق الأحرف في علامات اقتباس واحدة ('' ') بالقيمة الحرفية لكل حرف داخل علامات الاقتباس. قد لا يحدث اقتباس واحد بين علامات اقتباس واحدة، حتى عندما تسبق بخلفية خلفية.

لذلك لن يفهم باش:

alias x='y \'z '

ومع ذلك، يمكنك القيام بذلك إذا كنت تحيط بنقوط اقتباس مزدوجة:

alias x="echo \'y "
> x
> 'y

يمكنني تأكيد ذلك باستخدام '\'' بالنسبة إلى اقتباس واحد داخل سلسلة واحدة مقتبسة تعمل في Bash، ويمكن تفسيرها بنفس الطريقة مثل الوسيطة "الإلتصاق" من وقت سابق في الخيط. لنفترض أن لدينا سلسلة مقتبسة: 'A '\''B'\'' C' (جميع علامات الاقتباس هنا هي اقتباسات واحدة). إذا تم تمريره إلى صدى، فهو يطبع ما يلي: A 'B' Cوبعد في كل '\'' اقتباس الأول يغلق السلسلة الواحدة الحالية، ما يلي \' المواد اللاصقة اقتباس واحد إلى السلسلة السابقة (\' هي وسيلة لتحديد اقتباس واحد دون بدء سلسلة مقتبسة)، ويفتح الاقتباس الأخير سلسلة أخرى مقتبس.

مثال بسيط عن الهروب من اقتباسات في قذيفة:

$ echo 'abc'\''abc'
abc'abc
$ echo "abc"\""abc"
abc"abc

يتم ذلك عن طريق الانتهاء بالفعل فتح واحد (')، وضع هرب واحد (\')، ثم فتح واحد آخر ('). يعمل بناء الجملة هذا لجميع الأوامر. انها نهج مماثلة جدا للإجابة الأولى.

تعمل كلا الإصدارين، إما بسلسلة باستخدام حرف اقتباس واحد هرب ( ')، أو بسلسلة من خلال إرفاق حرف Quote واحد ضمن اقتباسات مزدوجة ("" "").

لم يلاحظ مؤلف السؤال أن هناك اقتباس واحد إضافي () في نهاية آخر محاولة هرب له:

alias rxvt='urxvt -fg'\''#111111'\'' -bg '\''#111111'\''
           │         │┊┊|       │┊┊│     │┊┊│       │┊┊│
           └─STRING──┘┊┊└─STRIN─┘┊┊└─STR─┘┊┊└─STRIN─┘┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      ┊┊         ┊┊       ┊┊         ┊┊│
                      └┴─────────┴┴───┰───┴┴─────────┴┘│
                          All escaped single quotes    │
                                                       │
                                                       ?

كما ترون في قطعة واحدة لطيفة السابقة من ASCII / Unicode Art، فإن آخر اقتباس واحد هارب ( '') متبوعا باقتباس واحد غير ضروري ('). باستخدام Syntax-Highlighter مثل الحاضر الموجود في المفكرة ++ يمكن أن يثبت مفيدة للغاية.

وينطبق الشيء نفسه على مثال آخر مثل الوحدة التالية:

alias rc='sed '"'"':a;N;$!ba;s/\n/, /g'"'"
alias rc='sed '\'':a;N;$!ba;s/\n/, /g'\'

تظهر هذان الحاليتان الجميلان من الأسماء المستعارة بطريقة معقدة للغاية ومباشرة كيف يمكن تصيح ملف. هذا هو، من ملف مع الكثير من الخطوط، تحصل على سطر واحد فقط مع فواصل ومسافات بين محتويات الأسطر السابقة. من أجل فهم التعليق السابق، فيما يلي مثال:

$ cat Little_Commas.TXT
201737194
201802699
201835214

$ rc Little_Commas.TXT
201737194, 201802699, 201835214

أنا لا أعلم عن مشكلة الاقتباس على وجه التحديد لأنه، حسنا، أحيانا، من المعقول النظر في نهج بديل.

rxvt() { urxvt -fg "#${1:-000000}" -bg "#${2:-FFFFFF}"; }

الذي يمكنك بعد ذلك الاتصال به:

rxvt 123456 654321

هذه الفكرة التي يمكنك الآن الآن الاسم المستعار هذا دون اهتمام الاقتباسات:

alias rxvt='rxvt 123456 654321'

أو، إذا كنت بحاجة إلى تضمين # في جميع المكالمات لسبب ما:

rxvt() { urxvt -fg "${1:-#000000}" -bg "${2:-#FFFFFF}"; }

الذي يمكنك بعد ذلك الاتصال به:

rxvt '#123456' '#654321'

ثم، بالطبع، الاسم المستعار هو:

alias rxvt="rxvt '#123456' '#654321'"

(عفوا، أعتقد أنني أعالج نوعا من الاقتباس :)

أنا فقط استخدام رموز قذيفة .. على سبيل المثال \x27 أو \\x22 حسب الاقتضاء. لا متاعب حقا.

نظرا لأن أحد لا يمكنه وضع علامات اقتباس واحدة داخل سلاسل واحدة مقتبسة، فإن الخيار أبسط والأكثر قابلية للقراءة هو استخدام سلسلة Heredoc

command=$(cat <<'COMMAND'
urxvt -fg '#111111' -bg '#111111'
COMMAND
)

alias rxvt=$command

في الكود أعلاه، يتم إرسال Heredoc إلى cat يتم تعيين الأمر وإخراج ذلك إلى متغير عبر تدوين استبدال الأمر $(..)

هناك حاجة وضع اقتباس واحد حول Heredoc لأنه داخل $()

IMHO الجواب الحقيقي هو أنه لا يمكنك الهروب من اقتباسات واحدة داخل سلاسل واحدة مقتبسة.

هذا مستحيل.

إذا نفترض أننا نستخدم باش.

من دليل باش ...

Enclosing characters in single quotes preserves the literal value of each
character within the quotes.  A single quote may not occur
between single quotes, even when preceded by a backslash.

تحتاج إلى استخدام واحدة من آليات الهروب من السلسلة الأخرى "أو

لا يوجد شيء سحر حول alias يطالب به استخدام اقتباسات واحدة.

كل من العمل التالي في باش.

alias rxvt="urxvt -fg '#111111' -bg '#111111'"
alias rxvt=urxvt\ -fg\ \'#111111\'\ -bg\ \'#111111\'

هذا الأخير يستخدم للهروب من شخصية الفضاء.

لا يوجد أيضا سحر سحر حول # 111111 يتطلب علامات اقتباس واحدة.

يحقق الخيارات التالية نفس النتيجة الخيارين الآخرين، في أن المستعار RXVT يعمل كما هو متوقع.

alias rxvt='urxvt -fg "#111111" -bg "#111111"'
alias rxvt="urxvt -fg \"#111111\" -bg \"#111111\""

يمكنك أيضا الهروب من مزعجة # مباشرة

alias rxvt="urxvt -fg \#111111 -bg \#111111"

معظم هذه الإجابات تضرب على القضية المحددة التي تسأل عنها. هناك نهج عام أن هناك صديقا وتطويره يسمح بالاقتباس التعسفي في حال كنت بحاجة إلى اقتباس أوامر باش من خلال طبقات متعددة من توسيع شل، على سبيل المثال، من خلال SSH، su -c, bash -c, ، إلخ. هناك أساسية واحدة بدائية تحتاجها، هنا في باش الأصلي:

quote_args() {
    local sq="'"
    local dq='"'
    local space=""
    local arg
    for arg; do
        echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
        space=" "
    done
}

هذا يفعل بالضبط ما يقوله: إنه يقتبس كل حجة لكل حجة بشكل فردي (بعد توسيع الباش، بالطبع):

$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'

يفعل الشيء الواضح لطبقة واحدة من التوسع:

$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2

(لاحظ أن الاقتباسات المزدوجة حولها $(quote_args ...) ضرورية لجعل النتيجة في حجة واحدة ل bash -c.) ويمكن استخدامها بشكل عام على الاقتباس بشكل صحيح من خلال طبقات متعددة من التوسع:

$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2

المثال أعلاه:

  1. يقتبس قذيفة كل حجة إلى الداخلية quote_args بشكل فردي ثم يجمع بين الإخراج الناتج في حجة واحدة مع عروض الأسعار المزدوجة الداخلية.
  2. قذيفة ونقلت bash, -c, ، والنتيجة التي اقتبست بالفعل من الخطوة 1، ثم تجمع بين النتيجة في حجة واحدة مع علامات الاقتباس الخارجية المزدوجة.
  3. يرسل تلك الفوضى كوسيطة إلى الخارجي bash -c.

هذه هي الفكرة باختصار. يمكنك القيام ببعض الأشياء المعقدة للغاية مع هذا، ولكن عليك أن تكون حريصا على ترتيب التقييم وحوالي يتم نقل الأساليب. على سبيل المثال، ما يلي تفعل الأشياء الخاطئة (لبعض التعريف "خطأ"):

$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure

في المثال الأول، يتوسع باش على الفور quote_args cd /; pwd 1>&2 إلى أمرين منفصلين، quote_args cd / و pwd 1>&2, ، وبالتالي فإن CWD لا يزال /tmp عندما pwd يتم تنفيذ الأمر. يوضح المثال الثاني مشكلة مماثلة للجليد. في الواقع، تحدث نفس المشكلة الأساسية مع جميع التوسعات باش. المشكلة هنا هي أن استبدال الأوامر ليس استدعاء دالة: يتم تقييمه حرفيا البرنامج النصي باش واحد واستخدام إخراجه كجزء من برنامج نصي باش آخر.

إذا حاولت ببساطة الهروب من مشغلي Shell، فسوف تفشل لأن السلسلة الناتجة مرت bash -c هو مجرد سلسلة من الأوتار التي نقلت بشكل فردي لا تفسر بعد ذلك كمشغلين، والذي يسهل معرفة ما إذا كنت صدى السلسلة التي كانت قد تم تمريرها إلى باش:

$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'

المشكلة هنا هي أنك قيد الاقتباس. ما تحتاجه هو عدم الافراج عن المشغلين كمدخلات للمحافين bash -c, وهذا يعني أنهم بحاجة إلى أن يكونوا خارج $(quote_args ...) استبدال القيادة.

وبالتالي، ما عليك القيام به في المعنى الأكثر فاعلا بشكل عام هو شل - اقتباس كل كلمة أمر لا يهدف إلى توسيعه في وقت استبدال الأوامر بشكل منفصل، وليس تطبيق أي اقتباس إضافي لمشغلي شل:

$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success

بمجرد القيام بذلك، تكون السلسلة بأكملها لعبة عادلة لمزيد من الاقتباس إلى مستويات التقييم التعسفية:

$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success

إلخ.

قد تبدو هذه الأمثلة جضية بالنظر إلى أن الكلمات مثل success, sbin, ، و pwd لا تحتاج إلى أن تكون قذيفة مقتبسة، ولكن النقطة الرئيسية لتتذكرها عند كتابة برنامج نصي يتناول الإدخال التعسفي هو أنك تريد اقتباس كل شيء غير متأكد تماما لا بحاجة إلى الاقتباس، لأنك لا تعرف أبدا عندما يرمي المستخدم في Robert'; rm -rf /.

لفهم أفضل ما يجري تحت الأغطية، يمكنك اللعب مع اثنين من وظائف المساعد الصغيرة:

debug_args() {
    for (( I=1; $I <= $#; I++ )); do
        echo -n "$I:<${!I}> " 1>&2
    done
    echo 1>&2
}

debug_args_and_run() {
    debug_args "$@"
    "$@"
}

من شأنها أن تعداد كل حجة أمر قبل تنفيذها:

$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''> 
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'> 
1:<echo> 2:<a"b'c> 3:<arg2> 
a"b'c arg2

في المثال المحدد، ما عليك سوى استخدام اقتباسات مزدوجة بدلا من علامات الاقتباس الفردية كآلية الهروب الخارجي:

alias rxvt="urxvt -fg '#111111' -bg '#111111'"

هذا النهج مناسب للعديد من الحالات التي تريد فيها فقط تمرير سلسلة ثابتة إلى أمر: فقط تحقق من كيفية تفسير قذيفة السلسلة المزدوجة من خلال echo, والهروب من الأحرف مع الخلفية إذا لزم الأمر.

في المثال، كنت ترى أن اقتباسات مزدوجة كافية لحماية السلسلة:

$ echo "urxvt -fg '#111111' -bg '#111111'"
urxvt -fg '#111111' -bg '#111111'

هنا هو وضع على واحد صحيح الجواب المشار إليه أعلاه:

في بعض الأحيان سوف يكون تحميل باستخدام رسينك عبر ssh و الهروب اسم الملف مع ' في ذلك مرتين!(OMG!) مرة باش ومرة ssh.نفس مبدأ التناوب اقتباس المحددات في العمل هنا.

على سبيل المثال, دعونا نقول أننا نريد الحصول على:لويس تيرو LA قصص ...

  1. أول كنت أرفق لويس تيرو في واحد يقتبس باش التنصيص على ssh:'"لويس تيرو"'
  2. ثم يمكنك استخدام علامات الاقتباس المفردة للهروب اقتباس مزدوجة '"'
  3. استخدام علامات الاقتباس المزدوجة للهروب الفاصلة "'"
  4. ثم كرر #2, باستخدام علامات الاقتباس المفردة للهروب اقتباس مزدوجة '"'
  5. ثم أرفق LA القصص في واحد يقتبس باش التنصيص على ssh:'"LA قصص"'

و ها!تصل الرياح مع هذا:

rsync -ave ssh '"Louis Theroux"''"'"'"'"''"s LA Stories"'

الذي هو الكثير من العمل مقابل القليل واحد ' -- ولكن هناك تذهب

طريقة أخرى لإصلاح مشكلة العديد من طبقات الاقتباس المتداخلة:

أنت تحاول الحشرات كثيرا في مساحة صغيرة جدا، لذلك استخدم وظيفة باش.

المشكلة هي أنك تحاول الحصول على العديد من مستويات التعشيش، وتكنولوجيا الاسم المستعار الأساسية ليست قوية بما يكفي لاستيعابها. استخدم وظيفة BASH مثل هذا لجعله حتى يتم التعامل مع علامات اقتباس واحدة مزدوجة ومرافقة في المعلمات بشكل طبيعي كما نتوقع:

lets_do_some_stuff() {
    tmp=$1                       #keep a passed in parameter.
    run_your_program $@          #use all your passed parameters.
    echo -e '\n-------------'    #use your single quotes.
    echo `date`                  #use your back ticks.
    echo -e "\n-------------"    #use your double quotes.
}
alias foobarbaz=lets_do_some_stuff

ثم يمكنك استخدام متغيراتك 1 و 2 دولارا واحدا ونقلت مزدوجة والقراد الخلفي دون القلق بشأن دالة الاسم المستعار التي تحطمت سلامتها.

يطبع البرنامج هذا:

el@defiant ~/code $ foobarbaz alien Dyson ring detected @grid 10385
alien Dyson ring detected @grid 10385
-------------
Mon Oct 26 20:30:14 EDT 2015
-------------
shell_escape () {
    printf '%s' "'${1//\'/\'\\\'\'}'"
}

شرح التنفيذ:

  • اقتباسات مزدوجة حتى نتمكن من إخراج التفاف اقتباسات واحدة واستخدام ${...} بناء الجملة

  • بحث باش واستبدال يشبه: ${varname//search/replacement}

  • نحن نبذل قصارى جهدنا ' مع '\''

  • '\'' ترميز واحدة ' مثل ذلك:

    1. ' ينتهي اقتباس واحد

    2. \' ترميز أ ' (مطلوب خطأ في الخلفية لأننا لسنا داخل اقتباسات)

    3. ' يبدأ اقتباس واحد مرة أخرى

    4. باش تسلسل تلقائيا سلاسل مع عدم وجود مساحة بيضاء بين

  • هناك \ قبل كل شيء \ و ' لأن هذا هو القواعد الهروبية ل ${...//.../...} .

string="That's "'#@$*&^`(@#'
echo "original: $string"
echo "encoded:  $(shell_escape "$string")"
echo "expanded: $(bash -c "echo $(shell_escape "$string")")"

PS دائما تشفير إلى سلاسل واحدة مقتبسة لأنها بطريقة أبسط من سلاسل مزدوجة المعروضة.

إذا كان لديك GNU متوازي مثبتا، فيمكنك استخدام اقتباسها الداخلي:

$ parallel --shellquote
L's 12" record
<Ctrl-D>
'L'"'"'s 12" record'
$ echo 'L'"'"'s 12" record'
L's 12" record

من الإصدار 20190222 يمكنك حتى --shellquote عدة مرات:

$ parallel --shellquote --shellquote --shellquote
L's 12" record
<Ctrl-D>
'"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
$ eval eval echo '"'"'"'"'"'"'L'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'s 12" record'"'"'"'"'"'"'
L's 12" record

سوف اقتبس السلسلة في جميع الأصداف المدعومة (ليس فقط bash).

هذه الوظيفة:

quote () 
{ 
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

يسمح نقلا عن ذلك ' في داخل '. وبعد استخدم هذا:

$ quote "urxvt -fg '#111111' -bg '#111111'"
'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''

إذا أصبح الخط إلى Quote يحصل أكثر تعقيدا، مثل اقتباسات مزدوجة مختلطة مع علامات اقتباس واحدة، فقد تصبح صعبة للغاية للحصول على السلسلة إلى الاقتباس داخل المتغير. عندما تظهر مثل هذه الحالات، اكتب الخط الدقيق الذي تحتاجه إلى اقتباس داخل البرنامج النصي (على غرار هذا).

#!/bin/bash

quote ()
{
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}

while read line; do
    quote "$line"
done <<-\_lines_to_quote_
urxvt -fg '#111111' -bg '#111111'
Louis Theroux's LA Stories
'single quote phrase' "double quote phrase"
_lines_to_quote_

سوف الإخراج:

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'''
'Louis Theroux'\''s LA Stories'
''\''single quote phrase'\'' "double quote phrase"'

كل سلاسل قليلة بشكل صحيح داخل اقتباسات واحدة.

من الواضح أنه سيكون من الأسهل ببساطة أن تحيط بنقلية مزدوجة، ولكن أين التحدي في ذلك؟ هنا هي الإجابة باستخدام علامات اقتباس واحدة فقط. أنا أستخدم متغير بدلا من alias لذلك من الأسهل الطباعة للحصول على إثبات، ولكنها هي نفسها باستخدام alias.

$ rxvt='urxvt -fg '\''#111111'\'' -bg '\''#111111'\'
$ echo $rxvt
urxvt -fg '#111111' -bg '#111111'

تفسير

المفتاح هو أنه يمكنك إغلاق اقتباس واحد وإعادة فتحه عدة مرات كما تريد. علي سبيل المثال foo='a''b' بالضبط مثل foo='ab'. وبعد حتى تتمكن من إغلاق الاقتباس الفردي، ورمي في اقتباس حرفي واحد \', ، ثم أعد فتح اقتباس واحد التالي.

مخطط انهيار

هذا الرسم البياني يجعل من الواضح باستخدام الأقواس لإظهار حيث يتم فتح اقتباسات واحدة وإغلاقها. ونقلت ليست "متداخلة" مثل الأقواس يمكن أن يكون. يمكنك أيضا الانتباه إلى تسليط الضوء على اللون، والذي يتم تطبيقه بشكل صحيح. السلاسل المقتبسة هي مارون، في حين \' أسود.

'urxvt -fg '\''#111111'\'' -bg '\''#111111'\'    # original
[^^^^^^^^^^] ^[^^^^^^^] ^[^^^^^] ^[^^^^^^^] ^    # show open/close quotes
 urxvt -fg   ' #111111  '  -bg   ' #111111  '    # literal characters remaining

(هذا هو الأساس نفس الإجابة مثل أدريان، لكنني أشعر أن هذا يفسر ذلك بشكل أفضل. أيضا إجابته لديها 2 اقتباسات مفردة غير ضرورية في النهاية.)

هنا حل آخر. ستستغرق هذه الوظيفة حجة واحدة واقتباسها بشكل مناسب باستخدام حرف Quote واحد، تماما كما يفسر الإجابة المعتمدة أعلاه:

single_quote() {
  local quoted="'"
  local i=0
  while [ $i -lt ${#1} ]; do
    local ch="${1:i:1}"
    if [[ "$ch" != "'" ]]; then
      quoted="$quoted$ch"
    else
      local single_quotes="'"
      local j=1
      while [ $j -lt ${#1} ] && [[ "${1:i+j:1}" == "'" ]]; do
        single_quotes="$single_quotes'"
        ((j++))
      done
      quoted="$quoted'\"$single_quotes\"'"
      ((i+=j-1))
    fi
    ((i++))
  done
  echo "$quoted'"
}

لذلك، يمكنك استخدامه بهذه الطريقة:

single_quote "1 2 '3'"
'1 2 '"'"'3'"'"''

x="this text is quoted: 'hello'"
eval "echo $(single_quote "$x")"
this text is quoted: 'hello'

إذا كنت تقوم بإنشاء سلسلة Shell داخل Python 2 أو Python 3، فإن ما يلي قد يساعد في عرض الحجج:

#!/usr/bin/env python

from __future__ import print_function

try:  # py3
    from shlex import quote as shlex_quote
except ImportError:  # py2
    from pipes import quote as shlex_quote

s = """foo ain't "bad" so there!"""

print(s)
print(" ".join([shlex_quote(t) for t in s.split()]))

هذا سوف يخرج:

foo ain't "bad" so there!
foo 'ain'"'"'t' '"bad"' so 'there!'
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top