พิมพ์ใหญ่อักขระตัวแรกในตัวแปรที่มี bash


162

ฉันต้องการตัวพิมพ์ใหญ่เพียงตัวอักษรตัวแรกในสตริงของฉันด้วยการทุบตี

foo="bar";

//uppercase first character

echo $foo;

ควรพิมพ์ "บาร์";


1
มันไม่ได้ซ้ำกันแน่นอน แต่หลายเทคนิคที่อธิบายไว้ในสตริงการแปลงเป็นตัวพิมพ์เล็กในการเขียนสคริปต์เชลล์ Bashสามารถใช้กับปัญหานี้ได้
pjh

คำตอบ:


148
foo="$(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})${foo:1}"

2
แม้จะซับซ้อนกว่าคำตอบที่ให้คะแนนดีที่สุด แต่อันนี้จริง ๆ แล้วว่า: 'ตัวอักษรตัวใหญ่ตัวแรกในตัวแปร' คำตอบที่ได้คะแนนดีที่สุดไม่มีผลลัพธ์นั้น ดูเหมือนว่าคำตอบง่าย ๆ ถูกยกขึ้นมาอย่างเต็มใจมากกว่าคำตอบที่ถูกต้องหรือไม่
Krzysztof Jabłoński

15
@ KrzysztofJabłoński: จริงๆแล้วคำตอบที่ได้คะแนนดีที่สุดด้านล่างจะให้ผลลัพธ์เช่นเดียวกับคำตอบภายใต้ Bash 4 คำตอบนี้มีค่าใช้จ่ายในการเรียก subshell (เชลล์อีกตัว) ค่าใช้จ่ายในการเรียกยูทิลิตี้ 'tr' (กระบวนการอื่น ไม่ใช่ Bash) ค่าใช้จ่ายในการใช้ here-doc / string (การสร้างไฟล์ชั่วคราว) และใช้การทดแทนสองรายการเปรียบเทียบกับคำตอบที่ได้คะแนนดีที่สุด หากคุณต้องเขียนรหัสดั้งเดิมให้พิจารณาใช้ฟังก์ชั่นแทน subshell
Steve

2
คำตอบนี้จะถือว่าอินพุตเป็นตัวพิมพ์เล็กทั้งหมด ตัวแปรนี้ไม่มีข้อสมมุติ: $ (tr '[: lower:]' '[: upper:]' <<< $ {foo: 0: 1}) $ (tr '[: upper:]' '[: lower: ] '<<< $ {foo: 1})
Itai Hanski

5
@ItaiHanski OP ไม่ได้ระบุสิ่งที่จะเกิดขึ้นกับตัวละครที่เหลืออยู่เพียงตัวแรก ควรที่พวกเขาต้องการที่จะเปลี่ยนแปลงเข้าnotQuiteCamel NotQuiteCamelชุดตัวเลือกของคุณมีผลข้างเคียงหรือบังคับให้ส่วนที่เหลือเป็นตัวพิมพ์เล็ก - ด้วยค่าใช้จ่ายในการเพิ่มจำนวนของ sub shells และกระบวนการ tr
Jesse Chisholm

3
@DanieleOrlando จริง แต่คำถามนี้ไม่มีแท็กอื่นนอกจากbashนี้
ชาร์ลส์ดัฟฟี่

314

วิธีหนึ่งในการทุบตี (เวอร์ชัน 4+):

foo=bar
echo "${foo^}"

พิมพ์:

Bar

2
ตรงข้ามกับสิ่งนี้หรือไม่?
CMCDragonkai

44
@CMCDragonkai: "${foo,}"เป็นตัวพิมพ์เล็กตัวอักษรตัวแรกใช้ "${foo,,}"เป็นตัวพิมพ์เล็กทุกตัวอักษรที่ใช้ "${foo^^}"เป็นตัวพิมพ์ใหญ่ทั้งหมดตัวอักษรที่ใช้
Steve

1
ฉันตั้งใจสังเกตเห็นว่า${foo~}และ${foo~~}มีผลเช่นเดียวกับ และ${foo^} ${foo^^}แต่ฉันไม่เคยเห็นทางเลือกที่กล่าวถึงทุกที่
mivk

5
ดูเหมือนว่าจะใช้งานไม่ได้กับ Mac รับข้อผิดพลาดต่อไปนี้:bad substitution
Toland Hon

1
@TolandHon: เนื่องจากคุณอาจได้ออกกำลังกายแล้วคุณจะต้องอัพเกรดเป็นรุ่น 4.x หรือลองวิธีแก้ไขปัญหาอื่นหากไม่สามารถอัพเกรดเครื่องด้วยเหตุผลบางประการ
Steve

29

ทางเดียวกับsed:

echo "$(echo "$foo" | sed 's/.*/\u&/')"

พิมพ์:

Bar

10
ไม่ทำงานบน MacOS10 Lion sed ถอนหายใจ \ u ขยายเป็น u แทนที่จะเป็นโอเปอเรเตอร์
vwvan

2
@vwvan และปฏิบัติตามคำแนะนำในการใช้คำสั่งโดยไม่มีคำนำหน้าbrew install coreutils gnu-sed gสำหรับสิ่งที่คุ้มค่าฉันไม่ต้องการเรียกใช้คำสั่งเหล่านี้ในเวอร์ชัน OSX ตั้งแต่รับรุ่น GNU
คณบดี

@vwvan: ใช่ขอโทษคุณจะต้อง GNU sedในกรณีนี้
Steve

2
@Dean: FWIW ฉันไม่เคยต้องการที่จะใช้ OSX :-)
Steve

5
เพียงแค่เปลี่ยนเป็นsed 's/./\U&/และมันจะทำงานบน POSIX sed
David Conrad


14

นี่คือวิธีใช้เครื่องมือข้อความ "ดั้งเดิม":

#!/bin/bash

string="abcd"
first=`echo $string|cut -c1|tr [a-z] [A-Z]`
second=`echo $string|cut -c2-`
echo $first$second

ในที่สุดบางสิ่งบางอย่างที่ทำงานร่วมกับตัวแปรและบน Mac! ขอบคุณ!
OZZIE

4
@DanieleOrlando ไม่สามารถพกพาได้อย่างที่หวัง tr [:lower:] [:upper:]เป็นตัวเลือกที่ดีกว่าหากต้องการทำงานเป็นภาษา / โลแคลที่รวมตัวอักษรที่ไม่อยู่ในa-zหรือA-Zช่วง
Charles Duffy


4

มันสามารถทำได้ใน bash บริสุทธิ์ด้วย bash-3.2 เช่นกัน:

# First, get the first character.
fl=${foo:0:1}

# Safety check: it must be a letter :).
if [[ ${fl} == [a-z] ]]; then
    # Now, obtain its octal value using printf (builtin).
    ord=$(printf '%o' "'${fl}")

    # Fun fact: [a-z] maps onto 0141..0172. [A-Z] is 0101..0132.
    # We can use decimal '- 40' to get the expected result!
    ord=$(( ord - 40 ))

    # Finally, map the new value back to a character.
    fl=$(printf '%b' '\'${ord})
fi

echo "${fl}${foo:1}"

วิธีการกำหนด foo? ฉันพยายามfoo = $1แต่ฉันเพิ่งได้รับ-bash: foo: command not found
OZZIE

1
@OZZIE ไม่มีการเว้นวรรค=ในงานที่ได้รับมอบหมาย นี่คือสิ่งที่shellcheck.netจะค้นหาให้คุณตามโปรแกรม
Charles Duffy

ฉันมักจะลืมเกี่ยวกับเชลล์สคริปท์ .. เฉพาะภาษาที่พื้นที่หนึ่ง (ก่อน / หลัง) สำคัญ ... ถอนหายใจ (หลามอย่างน้อย 4 ถ้าฉันจำได้ถูกต้อง)
OZZIE

@OZZIE มันมีความสำคัญเพราะถ้ามันไม่สำคัญคุณไม่สามารถส่งผ่าน=ข้อโต้แย้งไปยังโปรแกรม (มันจะรู้ได้อย่างไรว่าsomeprogram =กำลังทำงานอยู่someprogramหรือมอบหมายให้ตัวแปรชื่อsomeprogram)
Charles Duffy

3

มันก็ใช้ได้เช่นกัน ...

FooBar=baz

echo ${FooBar^^${FooBar:0:1}}

=> Baz
FooBar=baz

echo ${FooBar^^${FooBar:1:1}}

=> bAz
FooBar=baz

echo ${FooBar^^${FooBar:2:2}}

=> baZ

และอื่น ๆ

แหล่งที่มา:

Inroductions / Tutorials:


3

หากต้องการใช้คำแรกให้พิมพ์ใหญ่เท่านั้น:

foo='one two three'
foo="${foo^}"
echo $foo

โอ้สองสาม


ในการใช้ประโยชน์ทุก ๆ คำในตัวแปร:

foo="one two three"
foo=( $foo ) # without quotes
foo="${foo[@]^}"
echo $foo

O ne T wo T hree


(ทำงานใน bash 4+)


foo='one two three'; foo=$(for i in $foo; do echo -n "${i^} "; done) ความละเอียดที่สั้นกว่าสำหรับทุกคำ fooตอนนี้คือ "One Two Three"
vr286

ฉันจะใช้อักษรตัวใหญ่ 3 หรือ 4 ตัวแรกเป็นตัวพิมพ์ใหญ่ได้อย่างไร เช่นpqrs-123-v1ถึงPQRS-123-v1 ? หรือคุณอาจพูดว่าตัวอักษรทั้งหมดก่อนที่จะใส่ยัติภังค์ ...
Vicky Dev

2

ทางเลือกและวิธีการแก้ปัญหาที่สะอาดสำหรับทั้ง Linux และ OSX มันยังสามารถใช้กับตัวแปร bash

python -c "print(\"abc\".capitalize())"

ส่งคืน Abc


0

อันนี้ใช้ได้สำหรับฉัน:

ค้นหาไฟล์ * php ทั้งหมดในไดเรกทอรีปัจจุบันและแทนที่อักขระตัวแรกของชื่อไฟล์แต่ละชื่อเป็นตัวพิมพ์ใหญ่:

เช่น: test.php => Test.php

for f in *php ; do mv "$f" "$(\sed 's/.*/\u&/' <<< "$f")" ; done

4
คำถามเดิมเป็นสตริงและไม่มีการอ้างอิงถึงการเปลี่ยนชื่อไฟล์
AndySavage

0

เพื่อความสนุกที่นี่คุณคือ:

foo="bar";    

echo $foo | awk '{$1=toupper(substr($1,0,1))substr($1,2)}1'
# or
echo ${foo^}
# or
echo $foo | head -c 1 | tr [a-z] [A-Z]; echo $foo | tail -c +2
# or
echo ${foo:1} | sed -e 's/^./\B&/'

0

ไม่ตรงกับสิ่งที่ถาม แต่มีประโยชน์มาก

declare -u foo #When the variable is assigned a value, all lower-case characters are converted to upper-case.

foo=bar
echo $foo
BAR

และตรงกันข้าม

declare -l foo #When the variable is assigned a value, all upper-case characters are converted to lower-case.

foo=BAR
echo $foo
bar

-1
first-letter-to-lower () {
        str="" 
        space=" " 
        for i in $@
        do
                if [ -z $(echo $i | grep "the\|of\|with" ) ]
                then
                        str=$str"$(echo ${i:0:1} | tr  '[A-Z]' '[a-z]')${i:1}$space" 
                else
                        str=$str${i}$space 
                fi
        done
        echo $str
}
first-letter-to-upper-xc () {
        v-first-letter-to-upper | xclip -selection clipboard
}
first-letter-to-upper () {
        str="" 
        space=" " 
        for i in $@
        do
                if [ -z $(echo $i | grep "the\|of\|with" ) ]
                then
                        str=$str"$(echo ${i:0:1} | tr  '[a-z]' '[A-Z]')${i:1}$space" 
                else
                        str=$str${i}$space 
                fi
        done
        echo $str
}

ตัวอักษรตัวแรกถึงล่าง xc () {v-first-letter-to-lower-xc | คลิปบอร์ด xclip - เลือก}


-7

ถ้าอักขระตัวแรกไม่ใช่ตัวอักษร (แต่เป็นแท็บเว้นวรรคและเครื่องหมายคำพูดคู่ที่หลบหนี) เราควรทดสอบมันจนกว่าเราจะพบจดหมาย! ดังนั้น:

S='  \"ó foo bar\"'
N=0
until [[ ${S:$N:1} =~ [[:alpha:]] ]]; do N=$[$N+1]; done
#F=`echo ${S:$N:1} | tr [:lower:] [:upper:]`
#F=`echo ${S:$N:1} | sed -E -e 's/./\u&/'` #other option
F=`echo ${S:$N:1}
F=`echo ${F} #pure Bash solution to "upper"
echo "$F"${S:(($N+1))} #without garbage
echo '='${S:0:(($N))}"$F"${S:(($N+1))}'=' #garbage preserved

Foo bar
= \"Foo bar=

6
กรุณาเขียนรหัสทำความสะอาด! คุณกำลังขาดเครื่องหมายคำพูดทั่วสถานที่ คุณกำลังใช้ไวยากรณ์ที่ล้าสมัย (backticks); คุณกำลังใช้ไวยากรณ์ที่ล้าสมัย ( $[...]) คุณใช้วงเล็บที่ไร้ประโยชน์มากมาย คุณสมมติว่าสตริงนั้นประกอบด้วยตัวอักษรจากตัวอักษรละติน (ตัวอักษรเน้นเสียงหรือตัวอักษรในตัวอักษรอื่น ๆ ) คุณมีงานจำนวนมากที่จะทำให้ข้อมูลโค้ดนี้ใช้งานได้! (และเนื่องจากคุณใช้ regex คุณอาจใช้ regex เพื่อค้นหาตัวอักษรตัวแรกแทนที่จะวนซ้ำ)
gniourf_gniourf

6
ฉันคิดว่าคุณผิด มันเป็นความคิดเห็นที่จำเป็นซึ่งมาพร้อมกับ downvote อธิบายรูปแบบที่ผิดทั้งหมดและความเข้าใจผิดของคำตอบ ได้โปรดเรียนรู้จากความผิดพลาดของคุณ (และก่อนหน้านั้นลองทำความเข้าใจพวกเขา) แทนที่จะดื้อรั้น การเป็นเพื่อนที่ดีก็ขัดกับการปฏิบัติที่ไม่ดีเช่นกัน
gniourf_gniourf

1
หรือถ้าใช้ Bash ≥4คุณไม่จำเป็นต้องtr:if [[ $S =~ ^([^[:alpha:]]*)([[:alpha:]].*) ]]; then out=${BASH_REMATCH[1]}${BASH_REMATCH[2]^}; else out=$S; fi; printf '%s\n' "$out"
gniourf_gniourf

5
รหัสนี้ยังใช้งานไม่ได้ มันยังคงใช้ backticks แทนไวยากรณ์การแทนที่สมัยใหม่ มันยังมีคำพูดที่ไม่ตรงกัน มันยังคงมีคำพูดที่หายไปในสถานที่ที่พวกเขาทำในความเป็นจริงเรื่อง ; ฯลฯ ลองใส่อักขระกลมที่ล้อมรอบช่องว่างลงในข้อมูลการทดสอบของคุณหากคุณไม่เชื่อว่าคำพูดที่หายไปเหล่านั้นสร้างความแตกต่างและแก้ไขปัญหาที่shellcheck.netพบจนกว่ารหัสนี้จะหมดจด
ชาร์ลส์ดัฟฟี่

2
ในฐานะที่เป็นภาษาที่คุณยกมาจากunix.stackexchange.com/questions/68694/...สิ่งที่คุณพลาดคือการที่echo $fooเป็นตัวอย่างของสถานการณ์ที่หนึ่งแยกวิเคราะห์คาดว่ารายการของคำ ; ดังนั้นในecho ${F}เนื้อหาของคุณFคือการแยกสตริงและการขยายแบบกลม นั่นเป็นความจริงที่คล้ายกันสำหรับecho ${S:$N:1}ตัวอย่างและส่วนที่เหลือทั้งหมด ดูBashPitfalls #
ชาร์ลส์ดัฟฟี่
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.