วิธีขยายตัวแปรพิเศษด้วยตนเอง (เช่น ~ tilde) ใน bash


145

ฉันมีตัวแปรในสคริปต์ทุบตีของฉันซึ่งมีค่าเป็นดังนี้:

~/a/b/c

สังเกตว่ามันเป็นเครื่องหมายทิลเดอที่ไม่ขยาย เมื่อฉันทำ ls -lt กับตัวแปรนี้ (เรียกมันว่า $ VAR) ฉันไม่พบไดเร็กทอรีดังกล่าว ฉันต้องการให้ bash ตีความ / ขยายตัวแปรนี้โดยไม่ต้องดำเนินการ กล่าวอีกนัยหนึ่งฉันต้องการให้ bash เรียกใช้ eval แต่ไม่ได้เรียกใช้คำสั่งที่ประเมิน เป็นไปได้ในการทุบตี?

ฉันจะจัดการส่งสิ่งนี้ไปยังสคริปต์ของฉันโดยไม่ขยายได้อย่างไร ฉันส่งผ่านอาร์กิวเมนต์โดยรอบด้วยเครื่องหมายคำพูดคู่

ลองใช้คำสั่งนี้เพื่อดูว่าฉันหมายถึงอะไร:

ls -lt "~"

นี่คือสถานการณ์ที่ฉันอยู่ฉันต้องการให้ตัวหนอนขยาย กล่าวอีกนัยหนึ่งฉันควรแทนที่เวทมนตร์ด้วยอะไรเพื่อให้คำสั่งทั้งสองนี้เหมือนกัน:

ls -lt ~/abc/def/ghi

และ

ls -lt $(magic "~/abc/def/ghi")

โปรดทราบว่า ~ / abc / def / ghi อาจมีหรือไม่มีอยู่


4
คุณอาจพบว่าการขยาย Tilde ในเครื่องหมายคำพูดก็มีประโยชน์เช่นกัน มันเป็นส่วนใหญ่ evalแต่ไม่ทั้งหมดเลี่ยงการใช้
Jonathan Leffler

2
ตัวแปรของคุณได้รับการกำหนดด้วยเครื่องหมายทิลเดอร์ที่ไม่ได้ขยายอย่างไร บางทีสิ่งที่จำเป็นคือกำหนดตัวแปรนั้นด้วยเครื่องหมายคำพูดนอกเครื่องหมายทิลเดอร์ foo=~/"$filepath"หรือfoo="$HOME/$filepath"
Chad Skeeters

dir="$(readlink -f "$dir")"
Jack Wasey

คำตอบ:


104

เนื่องจากลักษณะของ StackOverflow ฉันไม่สามารถทำให้คำตอบนี้ไม่เป็นที่ยอมรับได้ แต่ในช่วง 5 ปีที่ฉันโพสต์สิ่งนี้มีคำตอบที่ดีกว่าคำตอบพื้นฐานที่ยอมรับและค่อนข้างแย่ (ฉันยังเด็กอย่าฆ่า ผม).

วิธีแก้ปัญหาอื่น ๆ ในชุดข้อความนี้เป็นวิธีแก้ปัญหาที่ปลอดภัยและดีกว่า โดยเฉพาะอย่างยิ่งฉันจะเลือกสองอย่างนี้:


คำตอบเดิมสำหรับวัตถุประสงค์ทางประวัติศาสตร์ (แต่โปรดอย่าใช้สิ่งนี้)

ถ้าผมไม่ผิดจะไม่ขยายได้โดยสคริปต์ทุบตีในลักษณะที่เพราะมันจะถือว่าเป็นสตริงตัวอักษร"~" "~"คุณสามารถบังคับให้ขยายด้วยevalวิธีนี้

#!/bin/bash

homedir=~
eval homedir=$homedir
echo $homedir # prints home path

หรือใช้${HOME}หากคุณต้องการโฮมไดเร็กทอรีของผู้ใช้


3
คุณมีวิธีแก้ไขเมื่อตัวแปรมีช่องว่างหรือไม่?
Hugo

34
ฉันพบว่า${HOME}น่าสนใจที่สุด มีเหตุผลใดที่จะไม่ให้คำแนะนำนี้เป็นคำแนะนำหลักของคุณ? ไม่ว่าในกรณีใด ๆ ขอบคุณ!
ปราชญ์

1
+1 - ฉันต้องการขยาย ~ $ some_other_user และ eval ทำงานได้ดีเมื่อ $ HOME ไม่ทำงานเพราะฉันไม่ต้องการบ้านของผู้ใช้ปัจจุบัน
olivecoder

13
การใช้evalเป็นคำแนะนำที่น่ากลัวมันแย่มากที่ได้รับการโหวตเพิ่มขึ้นมากมาย คุณจะพบปัญหาทุกประเภทเมื่อค่าของตัวแปรมีอักขระเมตาของเชลล์
user2719058

1
ฉันไม่สามารถจบความคิดเห็นของฉันได้ในเวลานั้นและฉันไม่ได้รับอนุญาตให้แก้ไขในภายหลัง ดังนั้นฉันรู้สึกขอบคุณ (ขอบคุณอีกครั้ง @birryree) สำหรับโซลูชันนี้เนื่องจากช่วยในบริบทเฉพาะของฉันในเวลานั้น ขอบคุณ Charles ที่ทำให้ฉันรู้
olivecoder

119

หากvarผู้ใช้ป้อนตัวแปรไม่evalควรใช้เพื่อขยายเครื่องหมายทิลเดอร์โดยใช้

eval var=$var  # Do not use this!

เหตุผลคือ: ผู้ใช้สามารถพิมพ์โดยบังเอิญ (หรือโดยจุดประสงค์) ตัวอย่างเช่นvar="$(rm -rf $HOME/)"อาจเกิดผลร้าย

วิธีที่ดีกว่า (และปลอดภัยกว่า) คือการใช้การขยายพารามิเตอร์ Bash:

var="${var/#\~/$HOME}"

8
คุณจะเปลี่ยน ~ userName / แทนที่จะเป็น ~ / ได้อย่างไร
aspergillusOryzae

3
วัตถุประสงค์ของการคืออะไร#ใน"${var/#\~/$HOME}"?
Jahid

4
@Jahid มันถูกอธิบายไว้ในคู่มือ $varมันบังคับให้ตัวหนอนเพียงตรงกับจุดเริ่มต้นของ
HåkonHægland

1
ขอบคุณ. (1) ทำไมเราต้องมีการ\~หลบหนี~? (2) การตอบของคุณอนุมานว่าเป็นตัวละครครั้งแรกใน~ $varเราจะเพิกเฉยต่อช่องว่างสีขาวชั้นนำได้$varอย่างไร?
ทิม

2
โปรดแก้ไขคำอธิบายในคำตอบของคุณว่า "$ {var / # ... }" เป็นไวยากรณ์ bash พิเศษที่จับคู่เฉพาะตอนต้นเท่านั้น ด้วย URL ฉันใช้ bash มาหลายสิบปีแล้ว แต่ไม่รู้จักอันนั้น นอกจากนี้ปัญหาที่อาจเกิดขึ้นกับ eval () คือการแทรกโค้ดที่เป็นอันตราย
smci

25

การปรับตัวเองจากคำตอบก่อนหน้าเพื่อทำสิ่งนี้อย่างมีประสิทธิภาพโดยไม่มีความเสี่ยงด้านความปลอดภัยที่เกี่ยวข้องกับeval:

expandPath() {
  local path
  local -a pathElements resultPathElements
  IFS=':' read -r -a pathElements <<<"$1"
  : "${pathElements[@]}"
  for path in "${pathElements[@]}"; do
    : "$path"
    case $path in
      "~+"/*)
        path=$PWD/${path#"~+/"}
        ;;
      "~-"/*)
        path=$OLDPWD/${path#"~-/"}
        ;;
      "~"/*)
        path=$HOME/${path#"~/"}
        ;;
      "~"*)
        username=${path%%/*}
        username=${username#"~"}
        IFS=: read -r _ _ _ _ _ homedir _ < <(getent passwd "$username")
        if [[ $path = */* ]]; then
          path=${homedir}/${path#*/}
        else
          path=$homedir
        fi
        ;;
    esac
    resultPathElements+=( "$path" )
  done
  local result
  printf -v result '%s:' "${resultPathElements[@]}"
  printf '%s\n' "${result%:}"
}

...ใช้เป็น...

path=$(expandPath '~/hello')

อีกวิธีหนึ่งวิธีที่ง่ายกว่าซึ่งใช้evalอย่างระมัดระวัง:

expandPath() {
  case $1 in
    ~[+-]*)
      local content content_q
      printf -v content_q '%q' "${1:2}"
      eval "content=${1:0:2}${content_q}"
      printf '%s\n' "$content"
      ;;
    ~*)
      local content content_q
      printf -v content_q '%q' "${1:1}"
      eval "content=~${content_q}"
      printf '%s\n' "$content"
      ;;
    *)
      printf '%s\n' "$1"
      ;;
  esac
}

4
ดูรหัสของคุณดูเหมือนว่าคุณกำลังใช้ปืนใหญ่เพื่อฆ่ายุง มีได้จะเป็นวิธีที่ง่ายมาก ..
จีโน่

2
@ จีโน่มีวิธีที่ง่ายกว่านี้แน่นอน คำถามคือมีวิธีที่ง่ายกว่าที่ปลอดภัยหรือไม่
Charles Duffy

2
@Gino ... ฉันไม่คิดว่าหนึ่งสามารถใช้printf %qในการหลบหนีทุกอย่าง แต่ตัวหนอนและจากนั้นใช้evalโดยไม่มีความเสี่ยง
Charles Duffy

1
@Gino, ... และดำเนินการอื่น ๆ
Charles Duffy

4
ปลอดภัยใช่ แต่ไม่สมบูรณ์มาก รหัสของฉันไม่ซับซ้อนเพื่อความสนุก - มันซับซ้อนเพราะการดำเนินการจริงที่ทำโดยการขยายตัวทิลเดอนั้นซับซ้อน
Charles Duffy

10

"$(printf "~/%q" "$dangerous_path")"วิธีที่ปลอดภัยในการใช้งานเป็น EVAL โปรดทราบว่าเป็นการทุบตีโดยเฉพาะ

#!/bin/bash

relativepath=a/b/c
eval homedir="$(printf "~/%q" "$relativepath")"
echo $homedir # prints home path

ดูคำถามนี้เพื่อดูรายละเอียด

นอกจากนี้โปรดทราบว่าภายใต้ zsh สิ่งนี้จะง่ายพอ ๆ กับ echo ${~dangerous_path}


echo ${~root}ไม่ให้ฉันแสดงผลบน zsh (mac os x)
Orwellophile

export test="~root/a b"; echo ${~test}
Gyscos

9

แล้วสิ่งนี้ล่ะ:

path=`realpath "$1"`

หรือ:

path=`readlink -f "$1"`

ดูดี แต่ไม่มี realpath ในเครื่อง Mac ของฉัน และคุณจะต้องเขียน path = $ (realpath "$ 1")
Hugo

สวัสดี @Hugo คุณสามารถรวบรวมของคุณเองrealpathคำสั่งในกรณีเซลเซียสสำหรับคุณสามารถสร้างปฏิบัติการrealpath.exeใช้ทุบตีและGCCgcc -o realpath.exe -x c - <<< $'#include <stdlib.h> \n int main(int c,char**v){char p[9999]; realpath(v[1],p); puts(p);}'จากบรรทัดคำสั่งนี้: Cheers
olibre

@Quuxplusone ไม่เป็นความจริงอย่างน้อยบน linux: realpath ~->/home/myhome
blueFast

iv'e ใช้มันกับเบียร์บน mac
nhed

1
@dangonfast <workingdir>/~นี้จะไม่ทำงานถ้าคุณตั้งค่าตัวหนอนเข้าไปในคำพูดผลที่ได้คือ
Murphy

7

การขยาย (ไม่มีการเล่นสำนวน) ในคำตอบของ Birryree และ halloleo: วิธีการทั่วไปคือการใช้evalแต่มาพร้อมกับคำเตือนที่สำคัญบางประการ ได้แก่ ช่องว่างและการเปลี่ยนทิศทางเอาต์พุต ( >) ในตัวแปร สิ่งต่อไปนี้ดูเหมือนจะใช้ได้กับฉัน:

mypath="$1"

if [ -e "`eval echo ${mypath//>}`" ]; then
    echo "FOUND $mypath"
else
    echo "$mypath NOT FOUND"
fi

ลองใช้กับอาร์กิวเมนต์ต่อไปนี้:

'~'
'~/existing_file'
'~/existing file with spaces'
'~/nonexistant_file'
'~/nonexistant file with spaces'
'~/string containing > redirection'
'~/string containing > redirection > again and >> again'

คำอธิบาย

  • ${mypath//>}แถบออกตัวละครที่สามารถบังคับไฟล์ระหว่าง >eval
  • การeval echo ...ขยายตัวของทิลเดอที่แท้จริงคืออะไร
  • เครื่องหมายอัญประกาศคู่รอบ-eอาร์กิวเมนต์มีไว้เพื่อรองรับชื่อไฟล์ที่มีช่องว่าง

บางทีอาจมีวิธีแก้ปัญหาที่หรูหรากว่านี้ แต่นี่คือสิ่งที่ฉันคิดได้


3
$(rm -rf .)คุณอาจพิจารณาดูที่พฤติกรรมที่มีชื่อที่มี
Charles Duffy

1
สิ่งนี้ไม่ทำลายเส้นทางที่มี>อักขระจริงหรือ?
Radon Rosborough

2

ฉันเชื่อว่านี่คือสิ่งที่คุณกำลังมองหา

magic() { # returns unexpanded tilde express on invalid user
    local _safe_path; printf -v _safe_path "%q" "$1"
    eval "ln -sf ${_safe_path#\\} /tmp/realpath.$$"
    readlink /tmp/realpath.$$
    rm -f /tmp/realpath.$$
}

ตัวอย่างการใช้งาน:

$ magic ~nobody/would/look/here
/var/empty/would/look/here

$ magic ~invalid/this/will/not/expand
~invalid/this/will/not/expand

ฉันประหลาดใจที่printf %q ไม่ได้หลบหนีตัวหนอนชั้นนำ - มันเกือบจะเป็นที่ดึงดูดที่จะยื่นเรื่องนี้เป็นข้อบกพร่องเนื่องจากเป็นสถานการณ์ที่ล้มเหลวตามวัตถุประสงค์ที่ระบุ อย่างไรก็ตามในระหว่างนี้การโทรที่ดี!
Charles Duffy

1
อันที่จริง - ข้อบกพร่องนี้ได้รับการแก้ไขในบางจุดระหว่าง 3.2.57 ถึง 4.3.18 ดังนั้นรหัสนี้จึงใช้งานไม่ได้
Charles Duffy

1
จุดดีฉันได้ปรับเป็นโค้ดเพื่อลบแกนนำ \ ถ้ามีอยู่ดังนั้นทั้งหมดคงที่และใช้งานได้ :) ฉันกำลังทดสอบโดยไม่อ้างอาร์กิวเมนต์ดังนั้นมันจึงขยายก่อนที่จะเรียกใช้ฟังก์ชัน
Orwellophile

1

นี่คือวิธีแก้ปัญหาของฉัน:

#!/bin/bash


expandTilde()
{
    local tilde_re='^(~[A-Za-z0-9_.-]*)(.*)'
    local path="$*"
    local pathSuffix=

    if [[ $path =~ $tilde_re ]]
    then
        # only use eval on the ~username portion !
        path=$(eval echo ${BASH_REMATCH[1]})
        pathSuffix=${BASH_REMATCH[2]}
    fi

    echo "${path}${pathSuffix}"
}



result=$(expandTilde "$1")

echo "Result = $result"

นอกจากนี้การพึ่งพาechoวิธีการที่expandTilde -nจะไม่ทำงานตามที่คาดไว้และพฤติกรรมที่มีชื่อไฟล์ที่มีแบ็กสแลชจะไม่ถูกกำหนดโดย POSIX ดูpubs.opengroup.org/onlinepubs/009604599/utilities/echo.html
Charles Duffy

จับดี. ปกติฉันใช้เครื่องผู้ใช้คนเดียวดังนั้นฉันจึงไม่คิดที่จะจัดการกับกรณีนั้น แต่ฉันคิดว่าฟังก์ชันนี้สามารถปรับปรุงได้อย่างง่ายดายเพื่อจัดการกับกรณีอื่น ๆ นี้โดยการ grepping ผ่านไฟล์ / etc / passwd สำหรับผู้ใช้อื่น จะปล่อยให้เป็นแบบฝึกหัดสำหรับคนอื่น :)
Gino

ฉันได้ทำแบบฝึกหัดนั้นแล้ว (และจัดการกรณี OLDPWD และอื่น ๆ ) ในคำตอบที่คุณคิดว่าซับซ้อนเกินไป :)
Charles Duffy

ที่จริงฉันเพิ่งพบวิธีแก้ปัญหาแบบบรรทัดเดียวที่ค่อนข้างง่ายซึ่งควรจัดการกับกรณีผู้ใช้อื่น: path = $ (eval echo $ orgPath)
Gino

1
FYI: ฉันเพิ่งอัปเดตโซลูชันเพื่อให้สามารถจัดการ ~ username ได้อย่างถูกต้อง และก็ควรจะปลอดภัยพอสมควรเช่นกัน แม้ว่าคุณจะใส่ '/ tmp / $ (rm -rf / *)' เป็นอาร์กิวเมนต์ แต่ก็ควรจัดการกับมันอย่างสง่างาม
Gino

1

เพียงใช้evalอย่างถูกต้อง: ด้วยการตรวจสอบความถูกต้อง

case $1${1%%/*} in
([!~]*|"$1"?*[!-+_.[:alnum:]]*|"") ! :;;
(*/*)  set "${1%%/*}" "${1#*/}"       ;;
(*)    set "$1" 
esac&& eval "printf '%s\n' $1${2+/\"\$2\"}"

นี่อาจปลอดภัย - ฉันไม่พบกรณีที่ล้มเหลว ที่กล่าวว่าถ้าเราจะพูดถึงการใช้ eval "อย่างถูกต้อง" ฉันขอยืนยันว่าคำตอบของ Orwellophile เป็นไปตามแนวทางปฏิบัติที่ดีกว่า: ฉันเชื่อว่าเชลล์printf %qจะหลบหนีสิ่งต่าง ๆ ได้อย่างปลอดภัยมากกว่าฉันเชื่อว่ารหัสตรวจสอบที่เขียนด้วยมือจะไม่มีข้อบกพร่อง .
Charles Duffy

@ ชาร์ลส์ดัฟฟี่ - โง่มาก เชลล์อาจไม่มี% q - และprintfเป็น$PATHคำสั่ง 'd
mikeserv

1
คำถามนี้ไม่ถูกแท็กbash? ถ้าเป็นเช่นนั้นprintfเป็นแบบ builtin และ%qรับประกันได้ว่าจะมีอยู่
Charles Duffy

@Charles Duffy - รุ่นอะไร?
mikeserv

1
@Charles Duffy - นั่น ... สวยเร็ว แต่ฉันยังคิดว่ามันแปลกที่คุณเชื่อ% q มากกว่าที่คุณจะเขียนโค้ดต่อหน้าต่อตา ive ใช้bashมากพอก่อนที่จะรู้ว่าจะไม่ไว้วางใจมัน ลอง:x=$(printf \\1); [ -n "$x" ] || echo but its not null!
mikeserv

1

ง่ายที่สุด : แทนที่ 'magic' ด้วย 'eval echo'

$ eval echo "~"
/whatever/the/f/the/home/directory/is

ปัญหา:คุณจะประสบปัญหากับตัวแปรอื่น ๆ เพราะ eval นั้นชั่วร้าย ตัวอย่างเช่น:

$ # home is /Users/Hacker$(s)
$ s="echo SCARY COMMAND"
$ eval echo $(eval echo "~")
/Users/HackerSCARY COMMAND

โปรดทราบว่าปัญหาของการฉีดไม่ได้เกิดขึ้นกับการขยายครั้งแรก ดังนั้นถ้าคุณจะแทนที่magicด้วยeval echoคุณก็น่าจะโอเค แต่ถ้าคุณทำเช่นecho $(eval echo ~)นั้นจะเสี่ยงต่อการฉีดยา

ในทำนองเดียวกันถ้าคุณทำeval echo ~แทนeval echo "~"นั่นจะนับเป็นการขยายสองครั้งดังนั้นจึงสามารถฉีดได้ทันที


1
ขัดกับสิ่งที่คุณกล่าวว่ารหัสนี้จะไม่ปลอดภัย s='echo; EVIL_COMMAND'ยกตัวอย่างเช่นการทดสอบ (มันจะล้มเหลวเพราะEVIL_COMMANDไม่มีอยู่ในคอมพิวเตอร์ของคุณ แต่ถ้าคำสั่งนั้นเป็นrm -r ~ตัวอย่างเช่นคำสั่งนั้นจะลบโฮมไดเร็กทอรีของคุณไป)
Konrad Rudolph

1

นี่คือฟังก์ชัน POSIX ที่เทียบเท่ากับคำตอบ Bash ของHåkonHægland

expand_tilde() {
    tilde_less="${1#\~/}"
    [ "$1" != "$tilde_less" ] && tilde_less="$HOME/$tilde_less"
    printf '%s' "$tilde_less"
}

2017-12-10 แก้ไข: เพิ่ม'%s'ต่อ @CharlesDuffy ในความคิดเห็น


1
printf '%s\n' "$tilde_less", บางที? มิฉะนั้นมันจะเกเรถ้าชื่อไฟล์ที่มีการขยายตัวมีเครื่องหมาย, %sหรือไวยากรณ์ของอื่น ๆ printfที่มีความหมายกับ นอกเหนือจากนั้นนี่เป็นคำตอบที่ดี - ถูกต้อง (เมื่อไม่จำเป็นต้องครอบคลุมส่วนขยาย bash / ksh) เห็นได้ชัดว่าปลอดภัย (ไม่มีการโคลนด้วยeval) และ terse
Charles Duffy

1

ทำไมไม่เจาะลึกถึงการรับโฮมไดเร็กทอรีของผู้ใช้ด้วย getent?

$ getent passwd mike | cut -d: -f6
/users/mike

0

เพียงเพื่อขยายคำตอบของbirryreeสำหรับเส้นทางที่มีช่องว่าง: คุณไม่สามารถใช้evalคำสั่งได้ตามที่เป็นอยู่เพราะมันแยกการประเมินตามช่องว่าง ทางออกหนึ่งคือการแทนที่ช่องว่างชั่วคราวสำหรับคำสั่ง eval:

mypath="~/a/b/c/Something With Spaces"
expandedpath=${mypath// /_spc_}    # replace spaces 
eval expandedpath=${expandedpath}  # put spaces back
expandedpath=${expandedpath//_spc_/ }
echo "$expandedpath"    # prints e.g. /Users/fred/a/b/c/Something With Spaces"
ls -lt "$expandedpath"  # outputs dir content

ตัวอย่างเช่นนี้ต้องอาศัยแน่นอนบนสมมติฐานที่ว่าไม่เคยมีลำดับถ่านmypath"_spc_"


1
ใช้ไม่ได้กับแท็บหรือบรรทัดใหม่หรือสิ่งอื่นใดใน IFS ... และไม่มีการรักษาความปลอดภัยรอบ ๆ ตัวอักษรเช่นเส้นทางที่มี$(rm -rf .)
Charles Duffy

0

คุณอาจพบว่าสิ่งนี้ทำได้ง่ายกว่าใน python

(1) จากบรรทัดคำสั่ง unix:

python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' ~/fred

ผลลัพธ์ใน:

/Users/someone/fred

(2) ภายใน bash script เป็นการปิดครั้งเดียว - บันทึกเป็นtest.sh:

#!/usr/bin/env bash

thepath=$(python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' $1)

echo $thepath

bash ./test.shผลการดำเนินการใน:

/Users/someone/fred

(3) เป็นยูทิลิตี้ - บันทึกสิ่งนี้ไว้ที่expanduserใดที่หนึ่งบนเส้นทางของคุณพร้อมสิทธิ์ดำเนินการ:

#!/usr/bin/env python

import sys
import os

print os.path.expanduser(sys.argv[1])

จากนั้นสามารถใช้ในบรรทัดคำสั่ง:

expanduser ~/fred

หรือในสคริปต์:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath

หรือจะส่งเฉพาะ '~' ไปยัง Python กลับ "/ home / fred" ได้อย่างไร
Tom Russell

1
ต้องการคำพูดคร่ำครวญ echo $thepathเป็นรถ; จำเป็นต้องecho "$thepath"แก้ไขกรณีที่ผิดปกติน้อยกว่า (ชื่อที่มีแท็บหรือการรันของช่องว่างที่ถูกแปลงเป็นช่องว่างเดียวชื่อที่มี globs มีการขยาย) หรือprintf '%s\n' "$thepath"แก้ไขสิ่งที่ผิดปกติด้วย (เช่นไฟล์ที่มีชื่อ-nหรือไฟล์ที่มี backslash literals บนระบบที่รองรับ XSI) ในทำนองเดียวกันthepath=$(expanduser "$1")
Charles Duffy

... เพื่อทำความเข้าใจว่าฉันหมายถึงอะไรเกี่ยวกับตัวอักษรแบ็กสแลชโปรดดูที่pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html - POSIX อนุญาตให้echoทำงานในลักษณะที่กำหนดการนำไปใช้อย่างสมบูรณ์หากอาร์กิวเมนต์ใด ๆ มีแบ็กสแลช ส่วนขยาย XSI ที่เป็นทางเลือกไปยัง POSIX กำหนดลักษณะการทำงานการขยายดีฟอลต์ (ไม่-eหรือ-Eจำเป็น) สำหรับชื่อดังกล่าว
Charles Duffy

0

ฉันได้ทำสิ่งนี้ด้วยการแทนที่พารามิเตอร์ตัวแปรหลังจากอ่านในพา ธ โดยใช้ read -e (ท่ามกลางคนอื่น ๆ ) ดังนั้นผู้ใช้จึงสามารถใส่แท็บเพื่อเติมเต็มเส้นทางและถ้าผู้ใช้ป้อน ~ พา ธ ก็จะถูกจัดเรียง

read -rep "Enter a path:  " -i "${testpath}" testpath 
testpath="${testpath/#~/${HOME}}" 
ls -al "${testpath}" 

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

(ฉันรวม -i สำหรับการอ่านเนื่องจากฉันใช้สิ่งนี้ในการวนซ้ำเพื่อให้ผู้ใช้สามารถแก้ไขเส้นทางได้หากมีปัญหา)

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