วิธีการแปลงสตริงเป็นตัวพิมพ์เล็กใน Bash?


1253

มีวิธีเข้าหรือไม่ การแปลงสตริงเป็นสตริงตัวพิมพ์เล็ก?

ตัวอย่างเช่นถ้าฉันมี:

a="Hi all"

ฉันต้องการแปลงเป็น:

"hi all"

1
ดูเพิ่มเติมที่ : stackoverflow.com/questions/11392189
dreftymac

คำตอบ:


2180

มีหลายวิธี:

มาตรฐาน POSIX

TR

$ echo "$a" | tr '[:upper:]' '[:lower:]'
hi all

AWK

$ echo "$a" | awk '{print tolower($0)}'
hi all

Non-POSIX

คุณอาจพบปัญหาการพกพาด้วยตัวอย่างต่อไปนี้:

Bash 4.0

$ echo "${a,,}"
hi all

sed

$ echo "$a" | sed -e 's/\(.*\)/\L\1/'
hi all
# this also works:
$ sed -e 's/\(.*\)/\L\1/' <<< "$a"
hi all

Perl

$ echo "$a" | perl -ne 'print lc'
hi all

ทุบตี

lc(){
    case "$1" in
        [A-Z])
        n=$(printf "%d" "'$1")
        n=$((n+32))
        printf \\$(printf "%o" "$n")
        ;;
        *)
        printf "%s" "$1"
        ;;
    esac
}
word="I Love Bash"
for((i=0;i<${#word};i++))
do
    ch="${word:$i:1}"
    lc "$ch"
done

หมายเหตุ: YMMV ในอันนี้ ใช้งานไม่ได้สำหรับฉัน (GNU bash รุ่น 4.2.46 และ 4.0.33 (และพฤติกรรมเดียวกัน 2.05b.0 แต่ไม่มีการใช้งาน nocasematch)) แม้ใช้งานshopt -u nocasematch;อยู่ การยกเลิกการตั้งค่า nocasematch จะทำให้ [["fooBaR" == "FOObar"]] จับคู่ OK BUT ในกรณีที่ [AZ] ถูกจับคู่อย่างไม่ถูกต้องโดย [AZ] Bash สับสนโดย double-negative ("nocasematch unsetting")! :-)


9
ฉันทำบางสิ่งบางอย่างหายไปหรือไม่หรือตัวอย่างล่าสุดของคุณ (เป็น Bash) ทำสิ่งที่แตกต่างอย่างสิ้นเชิงจริงหรือ มันทำงานได้สำหรับ "ABX" แต่ถ้าคุณแทนที่จะทำword="Hi All"เช่นตัวอย่างอื่น ๆ ก็จะส่งกลับไม่ได้ha hi allใช้งานได้กับตัวพิมพ์ใหญ่และข้ามตัวอักษรที่มีตัวพิมพ์เล็ก
jangosteve

26
โปรดทราบว่าเฉพาะtrและawkตัวอย่างที่ระบุไว้ในมาตรฐาน POSIX
Richard Hansen

178
tr '[:upper:]' '[:lower:]'จะใช้สถานที่ปัจจุบันเพื่อกำหนดเทียบเท่า / ตัวพิมพ์เล็กดังนั้นมันจะทำงานกับสถานที่ที่ใช้ตัวอักษรที่มีเครื่องหมายกำกับ
Richard Hansen

10
หนึ่งจะได้รับผลลัพธ์เป็นตัวแปรใหม่ได้อย่างไร คือฉันต้องการสตริงที่ลดลงในตัวแปรใหม่หรือไม่?
Adam Parkin

60
@Adam:b="$(echo $a | tr '[A-Z]' '[a-z]')"
Tino

434

ใน Bash 4:

เพื่อตัวพิมพ์เล็ก

$ string="A FEW WORDS"
$ echo "${string,}"
a FEW WORDS
$ echo "${string,,}"
a few words
$ echo "${string,,[AEIUO]}"
a FeW WoRDS

$ string="A Few Words"
$ declare -l string
$ string=$string; echo "$string"
a few words

หากต้องการตัวพิมพ์ใหญ่

$ string="a few words"
$ echo "${string^}"
A few words
$ echo "${string^^}"
A FEW WORDS
$ echo "${string^^[aeiou]}"
A fEw wOrds

$ string="A Few Words"
$ declare -u string
$ string=$string; echo "$string"
A FEW WORDS

สลับ (ไม่มีเอกสาร แต่สามารถกำหนดค่าได้ในเวลารวบรวม)

$ string="A Few Words"
$ echo "${string~~}"
a fEW wORDS
$ string="A FEW WORDS"
$ echo "${string~}"
a FEW WORDS
$ string="a few words"
$ echo "${string~}"
A few words

พิมพ์ใหญ่ (ไม่มีเอกสาร แต่สามารถเลือกกำหนดค่าได้ในเวลารวบรวม)

$ string="a few words"
$ declare -c string
$ string=$string
$ echo "$string"
A few words

กรณีชื่อ:

$ string="a few words"
$ string=($string)
$ string="${string[@]^}"
$ echo "$string"
A Few Words

$ declare -c string
$ string=(a few words)
$ echo "${string[@]}"
A Few Words

$ string="a FeW WOrdS"
$ string=${string,,}
$ string=${string~}
$ echo "$string"
A few words

ในการเปิดปิดแอตทริบิวต์การใช้งานdeclare +ตัวอย่างเช่นdeclare +c string. สิ่งนี้มีผลต่อการมอบหมายที่ตามมาไม่ใช่ค่าปัจจุบัน

declareตัวเลือกเปลี่ยนแอตทริบิวต์ของตัวแปร แต่ไม่ได้เนื้อหา การมอบหมายใหม่ในตัวอย่างของฉันอัพเดตเนื้อหาเพื่อแสดงการเปลี่ยนแปลง

แก้ไข:

เพิ่ม "สลับตัวอักษรตัวแรกโดยคำว่า" ( ${var~}) ตามที่แนะนำโดยghostdog74

แก้ไข:พฤติกรรมตัวหนอนที่ถูกต้องเพื่อให้ตรงกับ Bash 4.3


5
bizzare ค่อนข้าง, "^^" และ ",," ตัวดำเนินการไม่ทำงานกับอักขระที่ไม่ใช่ ASCII แต่ "~~" จะทำ ... ดังนั้นstring="łódź"; echo ${string~~}จะส่งคืน "ŁÓDŹ" แต่echo ${string^^}ส่งคืน "łóDź" LC_ALL=pl_PL.utf-8แม้จะอยู่ใน นั่นคือการใช้ bash 4.2.24
Hubert Kario

2
@HubertKario: มันแปลกดี มันเป็นเหมือนกันสำหรับผมในการทุบตี 4.0.33 en_US.UTF-8กับสายเดียวกันใน มันเป็นข้อผิดพลาดและฉันได้รายงานแล้ว
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

1
@HubertKario: echo "$string" | tr '[:lower:]' '[:upper:]'ลอง มันอาจจะแสดงความล้มเหลวเดียวกัน อย่างน้อยก็ไม่ใช่ปัญหาของ Bash
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

1
@DennisWilliamson: ใช่ฉันก็สังเกตเห็นเช่นกัน (ดูความคิดเห็นคำตอบ Shuvalov) ฉันแค่จะพูดว่า "สิ่งนี้มีไว้สำหรับ ASCII" แต่จากนั้นเป็นผู้ดำเนินการ "~~" ที่ทำงานได้ดังนั้นจึงไม่เหมือนรหัสและตารางการแปลที่ยังไม่มี ...
Hubert Kario

4
@HubertKario: ผู้ดูแลระบบ Bash ยอมรับข้อผิดพลาดและแจ้งว่าจะได้รับการแก้ไขในรุ่นถัดไป
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

123
echo "Hi All" | tr "[:upper:]" "[:lower:]"

4
@RichardHansen: ใช้trไม่ได้กับตัวละครที่ไม่ใช่ ACII ฉันมีชุดภาษาที่ถูกต้องและสร้างไฟล์ภาษา มีความคิดใดที่ฉันสามารถทำผิดได้บ้าง
Hubert Kario

FYI: สิ่งนี้ใช้ได้กับ Windows / Msys ข้อเสนอแนะอื่น ๆ บางอย่างไม่ได้
wasatchwizard

3
ทำไมถึง[:upper:]จำเป็น?
mgutt

77

tr :

a="$(tr [A-Z] [a-z] <<< "$a")"

AWK :

{ print tolower($0) }

ใจเย็น ๆ :

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/

2
+1 a="$(tr [A-Z] [a-z] <<< "$a")"ดูง่ายที่สุดสำหรับฉัน ฉันยังคงเป็นผู้เริ่มต้น ...
Sandeepan Nath

2
ฉันขอแนะนำsedวิธีแก้ปัญหา; ฉันทำงานในสภาพแวดล้อมที่ด้วยเหตุผลบางอย่างไม่มีtrแต่ฉันยังไม่พบระบบที่ไม่มีsedบวกมากเวลาฉันต้องการทำสิ่งนี้ฉันเพิ่งทำสิ่งอื่นในsedต่อไปดังนั้นสามารถโยง คำสั่งเข้าด้วยกันเป็นคำสั่งเดียว (ยาว)
Haravikk

2
ควรระบุนิพจน์วงเล็บเหลี่ยม ในtr [A-Z] [a-z] Aเปลือกอาจดำเนินการขยายชื่อไฟล์หากมีชื่อไฟล์ประกอบด้วยตัวอักษรเดียวหรือตั้งค่าnullgob tr "[A-Z]" "[a-z]" Aจะทำงานอย่างถูกต้อง
เดนนิส

2
@CamiloMartin เป็นระบบ BusyBox ที่ฉันมีปัญหานั้นโดยเฉพาะ Synology NASes แต่ฉันก็พบกับมันในระบบอื่น ๆ เช่นกัน ฉันทำสคริปต์เชลล์ข้ามแพลตฟอร์มจำนวนมากเมื่อเร็ว ๆ นี้และด้วยความต้องการที่ไม่มีการติดตั้งอะไรเพิ่มเติมมันทำให้สิ่งต่าง ๆ ยุ่งยากมาก! อย่างไรก็ตามฉันยังไม่ได้พบกับระบบโดยsed
Haravikk

2
โปรดทราบว่าtr [A-Z] [a-z]ไม่ถูกต้องในเกือบทุกภาษา ตัวอย่างเช่นในen-USสถานที่เกิดเหตุเป็นจริงช่วงเวลาA-Z AaBbCcDdEeFfGgHh...XxYyZ
fuz

44

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่ฉันทำคำตอบนี้สำหรับไซต์อื่นดังนั้นฉันคิดว่าฉันโพสต์ไว้ที่นี่:

บน -> ด้านล่าง : ใช้ python:

b=`echo "print '$a'.lower()" | python`

หรือทับทิม:

b=`echo "print '$a'.downcase" | ruby`

หรือ Perl (อาจเป็นที่ชื่นชอบ):

b=`perl -e "print lc('$a');"`

หรือ PHP:

b=`php -r "print strtolower('$a');"`

หรือ Awk:

b=`echo "$a" | awk '{ print tolower($1) }'`

หรือ Sed:

b=`echo "$a" | sed 's/./\L&/g'`

หรือทุบตี 4:

b=${a,,}

หรือ NodeJS หากคุณมี (และเป็นบิตถั่ว ... ):

b=`echo "console.log('$a'.toLowerCase());" | node`

คุณสามารถใช้dd(แต่ฉันจะไม่ทำ!):

b=`echo "$a" | dd  conv=lcase 2> /dev/null`

ด้านล่าง -> บน :

ใช้หลาม:

b=`echo "print '$a'.upper()" | python`

หรือทับทิม:

b=`echo "print '$a'.upcase" | ruby`

หรือ Perl (อาจเป็นที่ชื่นชอบ):

b=`perl -e "print uc('$a');"`

หรือ PHP:

b=`php -r "print strtoupper('$a');"`

หรือ Awk:

b=`echo "$a" | awk '{ print toupper($1) }'`

หรือ Sed:

b=`echo "$a" | sed 's/./\U&/g'`

หรือทุบตี 4:

b=${a^^}

หรือ NodeJS หากคุณมี (และเป็นบิตถั่ว ... ):

b=`echo "console.log('$a'.toUpperCase());" | node`

คุณสามารถใช้dd(แต่ฉันจะไม่ทำ!):

b=`echo "$a" | dd  conv=ucase 2> /dev/null`

นอกจากนี้เมื่อคุณพูดว่า 'เปลือก' ฉันถือว่าคุณหมายถึงbashแต่ถ้าคุณสามารถใช้zshมันก็ง่ายเหมือน

b=$a:l

สำหรับตัวพิมพ์เล็กและ

b=$a:u

สำหรับกรณีบน


@JESii ทั้งสองทำงานสำหรับฉันด้านบน -> ล่างและล่าง -> บน ฉันใช้ sed 4.2.2 และ Bash 4.3.42 (1) กับ Debian Stretch 64 บิต
nettux

1
สวัสดี @ nettux443 ... ฉันเพิ่งลองการดำเนินการทุบตีอีกครั้งและมันก็ล้มเหลวสำหรับฉันด้วยข้อความแสดงข้อผิดพลาด "การแทนที่ไม่ดี" ฉันใช้ OSX โดยใช้ bash ของ homebrew: GNU bash, รุ่น 4.3.42 (1) - ปล่อย (x86_64-apple-darwin14.5.0)
JESii

5
ไม่ได้ใช้! ตัวอย่างทั้งหมดที่สร้างสคริปต์นั้นเปราะมาก หากค่าของaมีคำพูดเดียวคุณไม่ได้ทำงานผิดปกติเท่านั้น แต่เป็นปัญหาด้านความปลอดภัยที่ร้ายแรง
tripleee

ฉันชอบวิธีการแก้ปัญหามากที่สุดเพราะ sed เป็นที่แพร่หลายอยู่เสมอ
Dudi Boy

ฉันชอบใช้โซลูชัน dd โปรดทราบว่าคุณจะต้องรูทเพื่อให้มันใช้งานได้
inetphantom



12

Pre Bash 4.0

Bash ลดขนาดตัวพิมพ์ของสตริงและกำหนดให้กับตัวแปร

VARIABLE=$(echo "$VARIABLE" | tr '[:upper:]' '[:lower:]') 

echo "$VARIABLE"

5
ไม่ต้องการechoและท่อ: ใช้$(tr '[:upper:]' '[:lower:]' <<<"$VARIABLE")
Tino

3
@Tino สตริงที่นี่ยังไม่สามารถเคลื่อนย้ายกลับไปเป็นเวอร์ชันเก่าของ Bash ได้ ฉันเชื่อว่ามันถูกนำมาใช้ใน v3
tripleee

1
@tripleee ถูกต้องมันถูกนำมาใช้ใน bash-2.05b - แต่นั่นเป็นทุบตีที่เก่าแก่ที่สุดที่ฉันสามารถหาได้ในระบบของฉัน
Tino

11

สำหรับเชลล์มาตรฐาน (โดยไม่มี bashisms) โดยใช้บิวอินเท่านั้น:

uppers=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lowers=abcdefghijklmnopqrstuvwxyz

lc(){ #usage: lc "SOME STRING" -> "some string"
    i=0
    while ([ $i -lt ${#1} ]) do
        CUR=${1:$i:1}
        case $uppers in
            *$CUR*)CUR=${uppers%$CUR*};OUTPUT="${OUTPUT}${lowers:${#CUR}:1}";;
            *)OUTPUT="${OUTPUT}$CUR";;
        esac
        i=$((i+1))
    done
    echo "${OUTPUT}"
}

และสำหรับตัวพิมพ์ใหญ่:

uc(){ #usage: uc "some string" -> "SOME STRING"
    i=0
    while ([ $i -lt ${#1} ]) do
        CUR=${1:$i:1}
        case $lowers in
            *$CUR*)CUR=${lowers%$CUR*};OUTPUT="${OUTPUT}${uppers:${#CUR}:1}";;
            *)OUTPUT="${OUTPUT}$CUR";;
        esac
        i=$((i+1))
    done
    echo "${OUTPUT}"
}

ฉันสงสัยว่าคุณจะไม่ปล่อยให้ความอัปยศในสคริปต์นี้เพราะมันไม่สามารถพกพาได้บน FreeBSD sh: $ {1: $ ... }: การทดแทนที่ไม่ดี
Dereckson

2
แท้จริง; สตริงย่อยที่${var:1:1}มี Bashism
tripleee

วิธีนี้มีการวัดประสิทธิภาพที่ไม่ดีพอ ดูคำตอบของฉันสำหรับการวัด
Dejay Clayton



7

การแสดงออกปกติ

ผมอยากจะใช้บัตรเครดิตสำหรับคำสั่งฉันต้องการที่จะแบ่งปัน แต่ความจริงก็คือผมได้รับสำหรับการใช้งานของตัวเองจากhttp://commandlinefu.com มันมีข้อได้เปรียบว่าถ้าคุณcdไปยังไดเรกทอรีใด ๆ ภายในโฟลเดอร์บ้านของคุณเองนั่นคือมันจะเปลี่ยนไฟล์และโฟลเดอร์ทั้งหมดเป็นตัวพิมพ์เล็กซ้ำ ๆ โปรดใช้ด้วยความระมัดระวัง เป็นการแก้ไขบรรทัดคำสั่งที่ยอดเยี่ยมและมีประโยชน์อย่างยิ่งสำหรับอัลบั้มที่คุณจัดเก็บไว้ในไดรฟ์

find . -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;

คุณสามารถระบุไดเรกทอรีแทนจุด (.) หลังจากค้นหาซึ่งหมายถึงไดเรกทอรีปัจจุบันหรือเส้นทางแบบเต็ม

ฉันหวังว่าโซลูชันนี้จะมีประโยชน์สิ่งหนึ่งที่คำสั่งนี้ไม่ได้ทำคือการแทนที่ช่องว่างด้วยขีดล่าง - โอ้ดีอีกครั้งบางที


สิ่งนี้ไม่ได้ผลสำหรับฉันไม่ว่าด้วยเหตุผลใดแม้ว่าจะดูดี ฉันได้รับสิ่งนี้ในการทำงานเป็นทางเลือกแม้ว่า: หา -exec / bin / bash -c 'mv {} `tr [AZ] [az] <<< {}`' \;
John Rix

ความต้องการนี้prenameจากperl: dpkg -S "$(readlink -e /usr/bin/rename)"ให้perl: /usr/bin/prename
Tino

4

Bashคำตอบที่หลายคนใช้โปรแกรมภายนอกซึ่งไม่ได้จริงๆใช้

หากคุณรู้ว่าคุณจะมี Bash4 คุณควรใช้${VAR,,}สัญกรณ์จริงๆ(มันง่ายและเท่ห์) สำหรับ Bash ก่อน 4 (ตัวอย่างเช่น My Mac ยังคงใช้ Bash 3.2) ฉันใช้คำตอบของ @ ghostdog74 เวอร์ชันที่แก้ไขเพื่อสร้างเวอร์ชันพกพามากขึ้น

หนึ่งที่คุณสามารถโทรlowercase 'my STRING'และรับรุ่นที่เล็กกว่า ฉันอ่านความคิดเห็นเกี่ยวกับการตั้งค่าผลลัพธ์เป็น var แต่นั่นไม่ใช่แบบพกพาจริงๆBashเนื่องจากเราไม่สามารถส่งคืนสตริงได้ การพิมพ์เป็นทางออกที่ดีที่สุด var="$(lowercase $str)"ง่ายต่อการจับกับสิ่งที่ต้องการ

วิธีนี้ใช้ได้ผล

วิธีการทำงานนี้คือโดยได้รับการแสดง ASCII จำนวนเต็มของแต่ละถ่านด้วยprintfแล้วadding 32ถ้าupper-to->lowerหรือถ้าsubtracting 32 lower-to->upperจากนั้นใช้printfอีกครั้งเพื่อแปลงตัวเลขกลับไปที่อักขระ จาก'A' -to-> 'a'เรามีความแตกต่าง 32 ตัวอักษร

ใช้printfเพื่ออธิบาย:

$ printf "%d\n" "'a"
97
$ printf "%d\n" "'A"
65

97 - 65 = 32

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

#!/bin/bash

# lowerupper.sh

# Prints the lowercase version of a char
lowercaseChar(){
    case "$1" in
        [A-Z])
            n=$(printf "%d" "'$1")
            n=$((n+32))
            printf \\$(printf "%o" "$n")
            ;;
        *)
            printf "%s" "$1"
            ;;
    esac
}

# Prints the lowercase version of a sequence of strings
lowercase() {
    word="$@"
    for((i=0;i<${#word};i++)); do
        ch="${word:$i:1}"
        lowercaseChar "$ch"
    done
}

# Prints the uppercase version of a char
uppercaseChar(){
    case "$1" in
        [a-z])
            n=$(printf "%d" "'$1")
            n=$((n-32))
            printf \\$(printf "%o" "$n")
            ;;
        *)
            printf "%s" "$1"
            ;;
    esac
}

# Prints the uppercase version of a sequence of strings
uppercase() {
    word="$@"
    for((i=0;i<${#word};i++)); do
        ch="${word:$i:1}"
        uppercaseChar "$ch"
    done
}

# The functions will not add a new line, so use echo or
# append it if you want a new line after printing

# Printing stuff directly
lowercase "I AM the Walrus!"$'\n'
uppercase "I AM the Walrus!"$'\n'

echo "----------"

# Printing a var
str="A StRing WITH mixed sTUFF!"
lowercase "$str"$'\n'
uppercase "$str"$'\n'

echo "----------"

# Not quoting the var should also work, 
# since we use "$@" inside the functions
lowercase $str$'\n'
uppercase $str$'\n'

echo "----------"

# Assigning to a var
myLowerVar="$(lowercase $str)"
myUpperVar="$(uppercase $str)"
echo "myLowerVar: $myLowerVar"
echo "myUpperVar: $myUpperVar"

echo "----------"

# You can even do stuff like
if [[ 'option 2' = "$(lowercase 'OPTION 2')" ]]; then
    echo "Fine! All the same!"
else
    echo "Ops! Not the same!"
fi

exit 0

และผลลัพธ์หลังจากใช้งานสิ่งนี้:

$ ./lowerupper.sh 
i am the walrus!
I AM THE WALRUS!
----------
a string with mixed stuff!
A STRING WITH MIXED STUFF!
----------
a string with mixed stuff!
A STRING WITH MIXED STUFF!
----------
myLowerVar: a string with mixed stuff!
myUpperVar: A STRING WITH MIXED STUFF!
----------
Fine! All the same!

สิ่งนี้ควรใช้ได้กับอักขระ ASCIIเท่านั้น

สำหรับฉันมันก็โอเคเพราะฉันรู้ว่าฉันจะผ่าน ASCII chars ไปเท่านั้น
ฉันกำลังใช้สิ่งนี้สำหรับตัวเลือก CLI ที่คำนึงถึงขนาดตัวพิมพ์ตัวอย่างเช่น


4

กรณีการแปลงจะทำสำหรับตัวอักษรเท่านั้น ดังนั้นควรทำงานอย่างเรียบร้อย

ฉันมุ่งเน้นการแปลงตัวอักษรระหว่าง az จากตัวพิมพ์ใหญ่เป็นตัวพิมพ์เล็ก ตัวละครอื่นใดที่ควรจะพิมพ์ใน stdout ตามที่มันเป็น ...

แปลงข้อความทั้งหมดในพา ธ / ถึง / ไฟล์ / ชื่อไฟล์ภายในช่วง az เป็น AZ

สำหรับการแปลงตัวเล็กเป็นตัวใหญ่

cat path/to/file/filename | tr 'a-z' 'A-Z'

สำหรับการแปลงจากตัวพิมพ์ใหญ่เป็นตัวพิมพ์เล็ก

cat path/to/file/filename | tr 'A-Z' 'a-z'

ตัวอย่างเช่น,

ชื่อไฟล์:

my name is xyz

ถูกแปลงเป็น:

MY NAME IS XYZ

ตัวอย่างที่ 2:

echo "my name is 123 karthik" | tr 'a-z' 'A-Z'
# Output:
# MY NAME IS 123 KARTHIK

ตัวอย่างที่ 3:

echo "my name is 123 &&^&& #@$#@%%& kAR2~thik" | tr 'a-z' 'A-Z'
# Output:
# MY NAME IS 123 &&^&& #@0@%%& KAR2~THIK

3

ถ้าใช้ v4 นี้จะอบใน ถ้าไม่นี่เป็นวิธีแก้ปัญหาที่ง่ายและใช้กันอย่างแพร่หลาย คำตอบอื่น ๆ (และความคิดเห็น) ในหัวข้อนี้มีประโยชน์มากในการสร้างรหัสด้านล่าง

# Like echo, but converts to lowercase
echolcase () {
    tr [:upper:] [:lower:] <<< "${*}"
}

# Takes one arg by reference (var name) and makes it lowercase
lcase () { 
    eval "${1}"=\'$(echo ${!1//\'/"'\''"} | tr [:upper:] [:lower:] )\'
}

หมายเหตุ:

  • ทำ: a="Hi All"แล้ว: lcase aจะทำเช่นเดียวกับ:a=$( echolcase "Hi All" )
  • ในฟังก์ชั่น lcase การใช้${!1//\'/"'\''"}แทนที่จะ${!1}อนุญาตให้สิ่งนี้ทำงานแม้ว่าสตริงจะมีเครื่องหมายคำพูด

3

สำหรับ Bash เวอร์ชั่นที่เก่ากว่า 4.0 เวอร์ชันนี้ควรเร็วที่สุด (เนื่องจากไม่แยก / เรียกใช้คำสั่งใด ๆ ):

function string.monolithic.tolower
{
   local __word=$1
   local __len=${#__word}
   local __char
   local __octal
   local __decimal
   local __result

   for (( i=0; i<__len; i++ ))
   do
      __char=${__word:$i:1}
      case "$__char" in
         [A-Z] )
            printf -v __decimal '%d' "'$__char"
            printf -v __octal '%03o' $(( $__decimal ^ 0x20 ))
            printf -v __char \\$__octal
            ;;
      esac
      __result+="$__char"
   done
   REPLY="$__result"
}

คำตอบของ technosaurusก็มีศักยภาพเช่นกันแม้ว่ามันจะทำงานได้อย่างถูกต้องสำหรับ mee ก็ตาม


ไม่เลว! สำหรับการวิเคราะห์ประสิทธิภาพของวิธีการนี้โปรดดูคำตอบของฉันสำหรับการวัด
Dejay Clayton

3

ทั้งๆที่วิธีการเดิมคำถามนี้และคล้ายกับคำตอบนี้โดย technosaurus ฉันมีปัญหาในการค้นหาโซลูชันที่สามารถพกพาข้ามแพลตฟอร์มส่วนใหญ่ (ที่ฉันใช้) และทุบตีรุ่นที่เก่ากว่า ฉันยังได้รับความผิดหวังกับอาร์เรย์ฟังก์ชั่นและการใช้งานของการพิมพ์ echos และไฟล์ชั่วคราวเพื่อดึงตัวแปรเล็ก ๆ น้อย ๆ มันใช้งานได้ดีมากสำหรับฉันจนฉันคิดว่าฉันจะแบ่งปัน สภาพแวดล้อมการทดสอบหลักของฉันคือ:

  1. ทุบตี GNU รุ่น 4.1.2 (1) - ปล่อย (x86_64-redhat-linux-gnu)
  2. GNU bash รุ่น 3.2.57 (1) - ปล่อย (sparc-sun-solaris2.10)
lcs="abcdefghijklmnopqrstuvwxyz"
ucs="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
input="Change Me To All Capitals"
for (( i=0; i<"${#input}"; i++ )) ; do :
    for (( j=0; j<"${#lcs}"; j++ )) ; do :
        if [[ "${input:$i:1}" == "${lcs:$j:1}" ]] ; then
            input="${input/${input:$i:1}/${ucs:$j:1}}" 
        fi
    done
done

สไตล์ Cง่ายๆ สำหรับการวนซ้ำเพื่อย้ำผ่านสตริง สำหรับสายด้านล่างหากคุณยังไม่เคยเห็นอะไรเช่นนี้มาก่อนที่ นี่คือที่ที่ผมได้เรียนรู้นี้ ในกรณีนี้บรรทัดจะตรวจสอบว่าอักขระถ่าน $ {input: $ i: 1} (ตัวพิมพ์เล็ก) มีอยู่ในอินพุตหรือไม่และถ้าเป็นเช่นนั้นจะแทนที่ด้วยอักขระที่กำหนด $ {ucs: $ j: 1} (ตัวพิมพ์ใหญ่) และเก็บไว้ กลับเข้ามา

input="${input/${input:$i:1}/${ucs:$j:1}}"

นี่เป็นสิ่งที่ไม่มีประสิทธิภาพอย่างมากวนซ้ำ 650 ครั้งในตัวอย่างของคุณด้านบนและใช้เวลา 35 วินาทีในการเรียกใช้ 1,000 การเรียกใช้บนเครื่องของฉัน สำหรับทางเลือกที่วนซ้ำเพียง 11 ครั้งและใช้เวลาน้อยกว่า 5 วินาทีในการเรียกใช้ 1,000 การเรียกดูคำตอบอื่น ๆ ของฉัน
Dejay Clayton

1
ขอบคุณแม้ว่ามันควรจะชัดเจนจากการมอง บางทีความผิดพลาดของหน้าเว็บอาจมาจากขนาดอินพุตและจำนวนการวนซ้ำที่คุณกำลังเรียกใช้งาน อย่างไรก็ตามฉันชอบทางออกของคุณ
JaredTS486

3

นี่เป็นวิธีที่แตกต่างกันอย่างรวดเร็วของวิธี JaredTS486ที่ใช้ความสามารถของ Bash ดั้งเดิม (รวมถึง Bash เวอร์ชัน <4.0) เพื่อปรับวิธีการของเขาให้เหมาะสม

ฉันได้กำหนดวิธีการนี้ 1,000 ครั้งสำหรับสตริงขนาดเล็ก (25 ตัวอักษร) และสตริงที่ใหญ่กว่า (445 ตัวอักษร) ทั้งสำหรับตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ เนื่องจากสตริงการทดสอบเป็นตัวพิมพ์เล็กส่วนใหญ่การแปลงเป็นตัวพิมพ์เล็กโดยทั่วไปจะเร็วกว่าตัวพิมพ์ใหญ่

ฉันได้เปรียบเทียบวิธีการของฉันกับคำตอบอื่น ๆ ในหน้านี้ที่เข้ากันได้กับ Bash 3.2 วิธีการของฉันมีประสิทธิภาพมากกว่าวิธีการส่วนใหญ่ที่บันทึกไว้ที่นี่และเร็วกว่าtrในหลายกรณี

นี่คือผลการจับเวลาสำหรับ 1,000 ซ้ำ 25 ตัวอักษร:

  • 0.46s สำหรับวิธีการของฉันในการพิมพ์เล็ก; 0.96 วินาทีสำหรับตัวพิมพ์ใหญ่
  • 1.16s สำหรับแนวทางของ Orwellophile ในการพิมพ์เล็ก 1.59s สำหรับตัวพิมพ์ใหญ่
  • 3.67s สำหรับtrตัวพิมพ์เล็ก; 3.81s สำหรับตัวพิมพ์ใหญ่
  • 11.12s สำหรับghostdog74's approachถึงตัวพิมพ์เล็ก; 31.41s สำหรับตัวพิมพ์ใหญ่
  • 26.25s สำหรับวิธีการของ technosaurus ในการพิมพ์เล็ก; 26.21s สำหรับตัวพิมพ์ใหญ่
  • 25.06s สำหรับแนวทางของ JaredTS486เพื่อตัวพิมพ์เล็ก 27.04 วินาทีสำหรับตัวพิมพ์ใหญ่

ผลการจับเวลาสำหรับ 1,000 ซ้ำจำนวน 445 ตัวอักษร (ประกอบด้วยบทกวี "The Robin" โดย Witter Bynner):

  • 2s สำหรับวิธีการของฉันในการพิมพ์เล็ก; 12 วินาทีสำหรับตัวพิมพ์ใหญ่
  • 4s สำหรับtrตัวพิมพ์เล็ก 4s สำหรับตัวพิมพ์ใหญ่
  • ยุค 20 สำหรับแนวทางของ Orwellophileเพื่อตัวพิมพ์เล็ก; 29 วินาทีสำหรับตัวพิมพ์ใหญ่
  • 75s สำหรับghostdog74'sเข้าใกล้ตัวพิมพ์เล็ก; 669s สำหรับตัวพิมพ์ใหญ่ เป็นเรื่องที่น่าสนใจที่จะสังเกตว่าความแตกต่างของประสิทธิภาพนั้นน่าทึ่งเพียงใดระหว่างการทดสอบที่มีการจับคู่ที่เด่นชัดกับการทดสอบที่มีการพลาดที่สำคัญ
  • 467s สำหรับวิธีการของ technosaurus ในการใช้ตัวพิมพ์เล็ก 449 วินาทีสำหรับตัวพิมพ์ใหญ่
  • 660sสำหรับแนวทางของ JaredTS486เพื่อตัวพิมพ์เล็ก 660 วินาทีสำหรับตัวพิมพ์ใหญ่ เป็นที่น่าสนใจที่จะทราบว่าวิธีการนี้สร้างความผิดพลาดของหน้าอย่างต่อเนื่อง (การแลกเปลี่ยนหน่วยความจำ) ใน Bash

สารละลาย:

#!/bin/bash
set -e
set -u

declare LCS="abcdefghijklmnopqrstuvwxyz"
declare UCS="ABCDEFGHIJKLMNOPQRSTUVWXYZ"

function lcase()
{
  local TARGET="${1-}"
  local UCHAR=''
  local UOFFSET=''

  while [[ "${TARGET}" =~ ([A-Z]) ]]
  do
    UCHAR="${BASH_REMATCH[1]}"
    UOFFSET="${UCS%%${UCHAR}*}"
    TARGET="${TARGET//${UCHAR}/${LCS:${#UOFFSET}:1}}"
  done

  echo -n "${TARGET}"
}

function ucase()
{
  local TARGET="${1-}"
  local LCHAR=''
  local LOFFSET=''

  while [[ "${TARGET}" =~ ([a-z]) ]]
  do
    LCHAR="${BASH_REMATCH[1]}"
    LOFFSET="${LCS%%${LCHAR}*}"
    TARGET="${TARGET//${LCHAR}/${UCS:${#LOFFSET}:1}}"
  done

  echo -n "${TARGET}"
}

วิธีการนั้นง่าย: ในขณะที่สตริงอินพุตมีตัวอักษรตัวพิมพ์ใหญ่ที่เหลืออยู่ให้ค้นหาตัวถัดไปและแทนที่อินสแตนซ์ทั้งหมดของตัวอักษรนั้นด้วยตัวแปรตัวพิมพ์เล็ก ทำซ้ำจนกระทั่งตัวอักษรตัวพิมพ์ใหญ่ทั้งหมดถูกแทนที่

คุณลักษณะด้านประสิทธิภาพบางอย่างของโซลูชันของฉัน:

  1. ใช้ยูทิลิตี้เชลล์ที่มีอยู่แล้วภายในซึ่งจะช่วยหลีกเลี่ยงค่าใช้จ่ายในการเรียกใช้ยูทิลิตี้ไบนารีภายนอกในกระบวนการใหม่
  2. หลีกเลี่ยง sub-shells ซึ่งจะมีการปรับประสิทธิภาพ
  3. ใช้กลไกเชลล์ที่รวบรวมและปรับให้เหมาะสมสำหรับประสิทธิภาพเช่นการแทนที่สตริงโกลบอลภายในตัวแปรการตัดส่วนต่อท้ายตัวแปรและการค้นหาและการจับคู่ regex กลไกเหล่านี้เร็วกว่าการวนซ้ำด้วยตนเองผ่านสตริง
  4. วนซ้ำตามจำนวนครั้งที่ต้องการโดยการนับจำนวนอักขระการจับคู่ที่ไม่ซ้ำกันที่จะถูกแปลง ตัวอย่างเช่นการแปลงสตริงที่มีอักขระตัวพิมพ์ใหญ่สามตัวเป็นตัวพิมพ์เล็กต้องใช้การวนซ้ำ 3 รอบเท่านั้น สำหรับตัวอักษร ASCII ที่กำหนดรูปแบบล่วงหน้าจำนวนการวนซ้ำวนซ้ำสูงสุดคือ 26
  5. UCSและLCSสามารถเติมได้ด้วยอักขระเพิ่มเติม

2

เพื่อจัดเก็บสตริงที่แปลงแล้วเป็นตัวแปร การติดตามทำงานให้ฉัน - $SOURCE_NAMEถึง$TARGET_NAME

TARGET_NAME="`echo $SOURCE_NAME | tr '[:upper:]' '[:lower:]'`"

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