كيفية تقسيم سلسلة واحدة إلى سلاسل متعددة مفصولة بمسافة واحدة على الأقل في bash shell؟



Answers

أنا أحب التحويل إلى مصفوفة ، لكي أتمكن من الوصول إلى العناصر الفردية:

    sentence="this is a story"
    stringarray=($sentence)

الآن يمكنك الوصول إلى العناصر الفردية مباشرة (تبدأ بـ 0):

    echo ${stringarray[0]}

أو تحويلها مرة أخرى إلى سلسلة من الحلقات:

    for i in "${stringarray[@]}"
    do
      :
      # do whatever on $i
    done

وبالطبع تمت الإجابة على الحلقات من خلال السلسلة مباشرة من قبل ، ولكن هذه الإجابة كان لها العيب في عدم تعقب العناصر الفردية لاستخدامها لاحقًا:

    for i in $sentence
    do
      :
      # do whatever on $i
    done

راجع أيضًا مرجع Bash Array

Question

لدي سلسلة تحتوي على العديد من الكلمات مع مسافة واحدة على الأقل بين كل منهما. كيف يمكنني تقسيم السلسلة إلى كلمات فردية حتى أتمكن من تكرارها من خلالهم؟

يتم تمرير السلسلة كوسيطة. مثال ${2} == "cat cat file" . كيف يمكنني التكرار من خلاله؟

أيضا ، كيف يمكنني التحقق مما إذا كانت سلسلة تحتوي على مسافات؟




(أ) لتقسيم جملة إلى كلماتها (فاصلة بين الفضاءين) يمكنك ببساطة استخدام IFS الافتراضي باستخدام

array=( $string )


مثال على تشغيل المقتطف التالي

#!/bin/bash

sentence="this is the \"sentence\"   'you' want to split"
words=( $sentence )

len="${#words[@]}"
echo "words counted: $len"

printf "%s\n" "${words[@]}" ## print array

سوف يخرج

words counted: 8
this
is
the
"sentence"
'you'
want
to
split

كما ترون ، يمكنك استخدام اقتباسات مفردة أو مزدوجة أيضًا دون أي مشكلة

ملاحظات:
- هذا هو في الأساس نفس إجابة mob ، لكن بهذه الطريقة تخزن المصفوفة لأي حاجة أخرى. إذا كنت تحتاج فقط إلى حلقة واحدة ، يمكنك استخدام إجابته ، وهو سطر واحد أقصر :)
- يرجى الرجوع إلى هذا السؤال لطرق بديلة لتقسيم سلسلة استنادًا إلى محدد.


(ب) للتحقق من وجود حرف في سلسلة ، يمكنك أيضًا استخدام مطابقة التعبير العادي.
مثال للتحقق من وجود حرف مساحة يمكنك استخدامه:

regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
    then
        echo "Space here!";
fi



الطريقة الأكثر سهولة وأمانًا في BASH 3 وما يليها هي:

var="string    to  split"
read -ra arr <<<"$var"

(حيث arr هو المصفوفة التي تأخذ الأجزاء المقسمة من السلسلة) ، أو إذا كانت هناك خطوط جديدة في الإدخال وتريد أكثر من السطر الأول فقط:

var="string    to  split"
read -ra arr -d '' <<<"$var"

(الرجاء ملاحظة المساحة في -d '' ، لا يمكن تركها بعيدًا) ، ولكن هذا قد يعطيك سطرًا جديدًا غير متوقع من <<<"$var" (لأن هذا يضيف ضمنيًا LF في النهاية).

مثال:

touch NOPE
var="* a  *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done

النتائج المتوقعة

[*]
[a]
[*]

لأن هذا الحل (على النقيض من جميع الحلول السابقة هنا) لا يميل إلى غليان غير متوقع وغير قابل للتحكم في الغالب.

كما يمنحك هذا القدرة الكاملة لـ IFS كما تريد:

مثال:

IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done

المخرجات شيء مثل:

[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]

كما ترى ، يمكن الحفاظ على المسافات بهذه الطريقة أيضًا:

IFS=: read -ra arr <<<' split  :   this    '
for a in "${arr[@]}"; do echo "[$a]"; done

المخرجات

[ split  ]
[   this    ]

يرجى ملاحظة أن التعامل مع IFS في BASH هو موضوع على حدة ، لذلك اختباراتك ، بعض المواضيع المثيرة للاهتمام في هذا:

  • unset IFS : Ignores تشغيل SPC و TAB و NL وعلى الخط يبدأ وينتهي
  • IFS='' : لا يوجد فصل للحقول ، فقط يقرأ كل شيء
  • IFS=' ' : تشغيل SPC (و SPC فقط)

بعض المثال الماضي

var=$'\n\nthis is\n\n\na test\n\n'
IFS=$'\n' read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done

المخرجات

1 [this is]
2 [a test]

في حين

unset IFS
var=$'\n\nthis is\n\n\na test\n\n'
read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done

المخرجات

1 [this]
2 [is]
3 [a]
4 [test]

راجع للشغل:

  • إذا لم تكن معتادًا على استخدام $'ANSI-ESCAPED-STRING' ، فسيكون ذلك بمثابة توقيت.

  • إذا لم تقم بتضمين -r (كما هو الحال في read -a arr <<<"$var" ) ، فيمكنك read -a arr <<<"$var" العكسية. هذا هو ترك التمرين للقارئ.

للسؤال الثاني:

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

case "$var" in
'')                empty_var;;                # variable is empty
*' '*)             have_space "$var";;        # have SPC
*[[:space:]]*)     have_whitespace "$var";;   # have whitespaces like TAB
*[^-+.,A-Za-z0-9]*) have_nonalnum "$var";;    # non-alphanum-chars found
*[-+.,]*)          have_punctuation "$var";;  # some punctuation chars found
*)                 default_case "$var";;      # if all above does not match
esac

لذا يمكنك تعيين قيمة الإرجاع للتحقق من SPC على النحو التالي:

case "$var" in (*' '*) true;; (*) false;; esac

لماذا case ؟ لأنه عادة ما يكون أكثر قابلية للقراءة من تسلسلات regex ، وبفضل العناصر الأولية لـ Shell ، فإنه يعالج 99٪ من جميع الاحتياجات بشكل جيد.




Related