การย้อนกลับเนื้อหาของตัวแปรด้วยคำพูด


13

ดังนั้นถ้าฉันมีตัวแปร

VAR='10 20 30 40 50 60 70 80 90 100'

และสะท้อนออกมา

echo "$VAR"
10 20 30 40 50 60 70 80 90 100

อย่างไรก็ตามยิ่งสคริปต์ฉันต้องย้อนกลับลำดับของตัวแปรนี้จึงแสดงเป็นสิ่งที่ต้องการ

echo "$VAR" | <code to reverse it>
100 90 80 70 60 50 40 30 20 10

ฉันพยายามใช้ rev และมันกลับด้านทุกอย่างดังนั้นมันจึงออกมาเป็น

echo "$VAR" | rev
001 09 08 07 06 05 04 03 02 01

2
คุณอาจพบคำแนะนำที่เป็นประโยชน์ในการตอบคำถามที่คล้ายกันนี้: จะอ่านที่อยู่ IP ได้อย่างไร (เพียงแค่ต้องปรับตัวคั่นช่องว่างแทนช่วงเวลา)
steeldriver

คำตอบ:


21

บนระบบ GNU การย้อนกลับของ cat คือ tac:

$ tac -s" " <<< "$VAR "            # Please note the added final space.
100 90 80 70 60 50 40 30 20 10

ไชโย! นั่นเป็นการหลอกลวง ฉันยังมีอะไรอีกมากมายให้เรียนรู้
อลิสแตร์ฮาร์ดี

@JhonKugelman หากไม่มี echo เอาต์พุตจะมีการขึ้นบรรทัดใหม่เพิ่มเติมซึ่งเทียบเท่ากับสิ่งนี้:echo $'100 \n90 80 70 60 50 40 30 20 10'
sorontar

ด้วยtac -s" " <<< "$VAR "เวอร์ชันปัจจุบันมันยังคงแทรกบรรทัดว่างนำหน้าเพิ่มช่องว่างต่อท้ายและละเว้นอักขระบรรทัดใหม่ต่อท้าย (ราวกับว่าprintf '\n%s ' "$reversed_VAR"แทนที่จะเป็นที่คาดไว้printf '%s\n' "$reversed_VAR")
Stéphane Chazelas

@don_crissti คำตอบดั้งเดิม: echo $(tac -s" " <<< $VAR)ไม่มีช่องว่างต่อท้ายเนื่องจากการ$(…)ลบบรรทัดใหม่และช่องว่างต่อท้ายหาก IFS มีพวกเขา โซลูชันของฉันจะได้รับการแก้ไขในไม่ช้าขอบคุณ
sorontar

6

สำหรับรายการสั้น ๆ และในตัวแปรเชลล์ตัวเองเป็นวิธีที่เร็วที่สุด:

var="10 20 30 40 50 60 70 80 90 100"
set -- $var; unset a 
for ((i=$#;i>0;i--)); do
    printf '%s%s' "${a:+ }" "${!i}"
    a=1
done
echo

เอาท์พุท:

100 90 80 70 60 50 40 30 20 10 

หมายเหตุ: การดำเนินการแยกในset -- $varความปลอดภัยสำหรับสตริงที่แน่นอนใช้ที่นี่ แต่จะไม่เป็นเช่นนั้นถ้าสตริงมีอักขระตัวแทน ( *, ?หรือ[]) มันอาจจะหลีกเลี่ยงset -fก่อนการแยกถ้าจำเป็น บันทึกเดียวกันนี้ยังใช้งานได้สำหรับโซลูชันต่อไปนี้ (ยกเว้นที่ระบุไว้)

หรือถ้าคุณต้องการตั้งค่าตัวแปรอื่น ๆ ด้วยรายการย้อนกลับ:

var="10 20 30 40 50 60 70 80 90 100"
set -- $var
for i; do out="$i${out:+ }$out"; done
echo "$out"

หรือใช้เฉพาะพารามิเตอร์ตำแหน่ง

var="10 20 30 40 50 60 70 80 90 100"
set -- $var
a=''
for    i
do     set -- "$i${a:+ $@}"
       a=1
done
echo "$1"

หรือใช้เพียงหนึ่งตัวแปร (ซึ่งอาจเป็น var เอง):
หมายเหตุ: วิธีนี้ไม่ได้รับผลกระทบจากการใช้ globing หรือ IFS

var="10 20 30 40 50 60 70 80 90 100"

a=" $var"
while [[ $a ]]; do
    printf '<%s>' "${a##* }"
    var="${a% *}"
done

2

awk ช่วยเหลือ

$ var='10 20 30 40 50 60 70 80 90 100'

$ echo "$var" | awk '{for(i=NF; i>0; i--) printf i==1 ? $i"\n" : $i" "}'
100 90 80 70 60 50 40 30 20 10


ด้วยperlมารยาทวิธีการอ่านที่อยู่ IP ไปข้างหลัง? แบ่งปันโดย @steeldriver

$ echo "$var" | perl -lane '$,=" "; print reverse @F'
100 90 80 70 60 50 40 30 20 10


หรือด้วยbashตัวเองโดยการแปลงสตริงเป็นอาเรย์

$ arr=($var)    
$ for ((i=${#arr[@]}-1; i>=0; i--)); do printf "${arr[i]} "; done; echo
100 90 80 70 60 50 40 30 20 10 

2

คุณสามารถใช้คุณสมบัติทุบตีเช่นอาร์เรย์เพื่อดำเนินการทั้งหมด:

#!/bin/bash
VAR="10 20 30 40 50 60 70 80 90 100"
a=($VAR); rev_a=()
for ((i=${#a[@]}-1;i>=0;i--)); do rev_a+=( ${a[$i]} ); done; 
RVAR=${rev_a[*]}; 

นี่น่าจะเป็นวิธีที่เร็วที่สุดที่จะทำหากว่า $ VAR ไม่ใหญ่มาก


อย่างไรก็ตามไม่ขยายไปถึงตัวแปรที่มีค่าประกอบด้วยอักขระตัวแทน (เช่นVAR='+ * ?') ใช้set -o noglobหรือset -fเพื่อแก้ไข โดยทั่วไปแล้วเป็นความคิดที่ดีที่จะพิจารณาถึงคุณค่า$IFSและสถานะของการโค้งเมื่อใช้ตัวดำเนินการแยก + glob ไม่ว่ามันจะทำให้ความรู้สึกเล็ก ๆ น้อย ๆ ที่จะใช้ประกอบในการที่rev_a+=( ${a[$i]} ), rev_a+=( "${a[$i]}" )จะทำให้รู้สึกมากขึ้น
Stéphane Chazelas


1

ใช้เวทมนตร์ Python สักเล็กน้อยที่นี่:

bash-4.3$ VAR='10 20 30 40 50 60 70 80 90 100'
bash-4.3$ python -c 'import sys;print " ".join(sys.argv[1].split()[::-1])'  "$VAR" 
100 90 80 70 60 50 40 30 20 10

วิธีการทำงานค่อนข้างง่าย:

  • เราอ้างถึงพารามิเตอร์"$VAR"บรรทัดคำสั่งที่สองsys.argv[1](พารามิเตอร์-cตัวแรกที่มีตัวเลือกอยู่เสมอ - -cตัวเองสังเกตการอ้างอิงรอบ ๆ"$VAR"เพื่อป้องกันการแยกตัวแปรตัวเองออกเป็นคำแต่ละคำ
  • ต่อไปเราจะใช้.split()วิธีการเพื่อแบ่งมันเป็นรายการสตริง
  • [::-1]ส่วนที่เป็นเพียงการขยายไวยากรณ์ชิ้น เพื่อกลับรายการ
  • ในที่สุดเราก็รวมทุกอย่างเข้าด้วยกันเป็นสตริงเดียว" ".join()และพิมพ์

แต่คนที่ไม่ใช่แฟนงูใหญ่เราสามารถใช้AWKทำสิ่งเดียวกันโดยพื้นฐานแล้วแยกและพิมพ์อาเรย์กลับด้าน:

 echo "$VAR" | awk '{split($0,a);x=length(a);while(x>-1){printf "%s ",a[x];x--};print ""}'
100 90 80 70 60 50 40 30 20 10 

1
หรือค่อนข้างจะสะอาดกว่าpython3 -c 'import sys;print(*reversed(sys.argv[1:]))' $VAR
iruvar

@iruvar ไม่สะอาดกว่าที่จะเรียกใช้ตัวดำเนินการแยก + glob ของเชลล์โดยไม่ต้องแน่ใจว่า$IFSมีอักขระที่เหมาะสมและไม่ปิดการใช้งานส่วน glob
Stéphane Chazelas

@ StéphaneChazelasจริงพอ - ส่วนของงูหลามยังคงสะอาดอยู่ (IMHO) ดังนั้นกลับไปที่"$VAR"อัตราผลตอบแทนpython3 -c 'import sys;print(*reversed(sys.argv[1].split()))' "$VAR"
iruvar

1

zsh:

print -r -- ${(Oa)=VAR}

$=VARแยกบน$VAR สั่งรายการผลลัพธ์ในลำดับอาเรย์กลับ (เหมือนใน) เหมือนกับ( เฉพาะ) เป็นรุ่นที่เชื่อถือได้ของ : พิมพ์อาร์กิวเมนต์ตามที่ - ถูกคั่นด้วยช่องว่างสิ้นสุดด้วยการขึ้นบรรทัดใหม่$IFS(Oa)print -r --kshecho -E -zshecho

หากคุณต้องการแยกพื้นที่เท่านั้นและไม่ได้อยู่ในสิ่งที่$IFSมี (ช่องว่างแท็บบรรทัดใหม่ศูนย์โดยค่าเริ่มต้น) กำหนดพื้นที่ให้$IFSหรือใช้การแยกอย่างชัดเจนเช่น:

print -r -- ${(Oas: :)VAR}

วิธีเรียงลำดับตัวเลขย้อนกลับ:

$ VAR='50 10 20 90 100 30 60 40 70 80'
$ print -r -- ${(nOn)=VAR}
100 90 80 70 60 50 40 30 20 10

POSIXly (ดังนั้นจะทำงานร่วมกับbash):

ด้วย shell builtin (ยกเว้นprintfในบางเชลล์) กลไกเท่านั้น (ดีกว่าสำหรับตัวแปรที่มีค่าสั้น):

unset -v IFS # restore IFS to its default value of spc, tab, nl
set -o noglob # disable glob
set -- $VAR # use the split+glob operator to assign the words to $1, $2...

reversed_VAR= sep=
for i do
  reversed_VAR=$i$sep$reversed_VAR
  sep=' '
done
printf '%s\n' "$reversed_VAR"

ด้วยawk(ดีกว่าสำหรับตัวแปรขนาดใหญ่โดยเฉพาะอย่างยิ่งด้วยbashแต่จนถึงข้อ จำกัด ของขนาดของอาร์กิวเมนต์ (หรืออาร์กิวเมนต์เดี่ยว):

 awk '
   BEGIN {
     n = split(ARGV[1], a);
     while (n) {printf "%s", sep a[n--]; sep = " "}
     print ""
   }' "$VAR"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.