ลบรายการ $ PATH ซ้ำด้วยคำสั่ง awk


48

ฉันพยายามเขียนฟังก์ชัน bash shell ที่จะอนุญาตให้ฉันลบสำเนาของไดเรกทอรีที่ซ้ำกันออกจากตัวแปรสภาพแวดล้อม PATH ของฉัน

ฉันถูกบอกว่าเป็นไปได้ที่จะบรรลุสิ่งนี้ด้วยคำสั่งหนึ่งบรรทัดโดยใช้awkคำสั่ง แต่ฉันไม่สามารถหาวิธีที่จะทำได้ มีใครรู้บ้าง



คำตอบ:


37

หากคุณยังไม่มีรายการที่ซ้ำกันในPATHและคุณต้องการเพิ่มไดเรกทอรีหากยังไม่มีอยู่คุณสามารถทำได้อย่างง่ายดายด้วยเชลล์เพียงอย่างเดียว

for x in /path/to/add …; do
  case ":$PATH:" in
    *":$x:"*) :;; # already there
    *) PATH="$x:$PATH";;
  esac
done

$PATHและนี่คือตัวอย่างที่เอาเปลือกที่ซ้ำกันจาก มันจะผ่านรายการหนึ่งต่อหนึ่งและคัดลอกรายการที่ยังไม่ได้เห็น

if [ -n "$PATH" ]; then
  old_PATH=$PATH:; PATH=
  while [ -n "$old_PATH" ]; do
    x=${old_PATH%%:*}       # the first remaining entry
    case $PATH: in
      *:"$x":*) ;;          # already there
      *) PATH=$PATH:$x;;    # not there yet
    esac
    old_PATH=${old_PATH#*:}
  done
  PATH=${PATH#:}
  unset old_PATH x
fi

มันจะดีกว่าถ้าวนซ้ำรายการใน $ PATH ย้อนกลับเนื่องจากรายการที่ใหม่กว่ามักจะถูกเพิ่มเข้ามาใหม่และพวกเขาอาจมีมูลค่าเป็นปัจจุบัน
Eric Wang

2
@EricWang ฉันไม่เข้าใจเหตุผลของคุณ องค์ประกอบ PATH ถูกข้ามจากด้านหน้าไปด้านหลังดังนั้นเมื่อมีการทำซ้ำการทำซ้ำที่สองจะถูกละเว้นอย่างมีประสิทธิภาพ การวนซ้ำจากหลังไปด้านหน้าจะเปลี่ยนลำดับ
Gilles 'หยุดชั่วร้าย'

@Gilles เมื่อคุณมีตัวแปรที่ซ้ำกันใน PATH อาจจะถูกเพิ่มด้วยวิธีนี้: PATH=$PATH:x=bx ใน PATH ดั้งเดิมอาจมีค่า a ดังนั้นเมื่อ iterate ตามลำดับดังนั้นค่าใหม่จะถูกละเว้น แต่เมื่อเรียงกลับกันใหม่ ค่าจะมีผล
Eric Wang

4
@EricWang ในกรณีนั้นมูลค่าเพิ่มไม่มีผลดังนั้นควรละเว้น เมื่อย้อนกลับไปคุณจะทำให้มูลค่าเพิ่มมาก่อน PATH=x:$PATHหากมูลค่าเพิ่มที่ได้รับควรจะไปก่อนที่มันจะได้รับการบันทึกเป็น
Gilles 'หยุดชั่วร้าย'

@Gilles เมื่อคุณต่อท้ายบางสิ่งนั่นหมายความว่ายังไม่มีหรือคุณต้องการแทนที่ค่าเก่าดังนั้นคุณต้องทำให้ตัวแปรที่เพิ่มเข้ามาใหม่สามารถมองเห็นได้ และโดยการประชุมมักจะมันผนวกในวิธีนี้ไม่ได้PATH=$PATH:... PATH=...:$PATHดังนั้นจึงเหมาะสมกว่าที่จะวนคำสั่งกลับรายการ แม้ว่าวิธีการของคุณจะใช้งานได้ก็ตามผู้คนจะต่อท้ายด้วยวิธีย้อนกลับ
Eric Wang

23

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

PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"

มันแยกบนโคลอน ( split(/:/, $ENV{PATH})) ใช้grep { not $seen{$_}++ }ในการกรองอินสแตนซ์ของเส้นทางที่ซ้ำ ๆ ยกเว้นการเกิดครั้งแรกจากนั้นรวมส่วนที่เหลือกลับมารวมกันโดยคั่นด้วยโคลอนและพิมพ์ผลลัพธ์ ( print join(":", ...))

หากคุณต้องการโครงสร้างเพิ่มเติมรอบ ๆ เช่นเดียวกับความสามารถในการขจัดความซ้ำซ้อนของตัวแปรอื่นเช่นกันลองตัวอย่างนี้ซึ่งฉันกำลังใช้ใน config ของฉัน:

# Deduplicate path variables
get_var () {
    eval 'printf "%s\n" "${'"$1"'}"'
}
set_var () {
    eval "$1=\"\$2\""
}
dedup_pathvar () {
    pathvar_name="$1"
    pathvar_value="$(get_var "$pathvar_name")"
    deduped_path="$(perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, $ARGV[0]))' "$pathvar_value")"
    set_var "$pathvar_name" "$deduped_path"
}
dedup_pathvar PATH
dedup_pathvar MANPATH

รหัสนั้นจะซ้ำซ้อนทั้ง PATH และ MANPATH และคุณสามารถเรียกdedup_pathvarตัวแปรอื่น ๆ ที่เก็บรายการเส้นทางที่คั่นด้วยโคลอน (เช่น PYTHONPATH) ได้อย่างง่ายดาย


ด้วยเหตุผลบางอย่างฉันต้องเพิ่ม a chompเพื่อลบ newline ที่ต่อท้าย สิ่งนี้ใช้ได้กับฉัน:perl -ne 'chomp; print join(":", grep { !$seen{$_}++ } split(/:/))' <<<"$PATH"
HåkonHægland

12

นี่คือรูปแบบเพรียวบาง:

printf %s "$PATH" | awk -v RS=: -v ORS=: '!arr[$0]++'

อีกต่อไป (เพื่อดูว่ามันทำงานอย่างไร):

printf %s "$PATH" | awk -v RS=: -v ORS=: '{ if (!arr[$0]++) { print $0 } }'

ตกลงเนื่องจากคุณยังใหม่กับ linux ต่อไปนี้เป็นวิธีตั้งค่า PATH โดยไม่มีการต่อท้าย ":"

PATH=`printf %s "$PATH" | awk -v RS=: '{ if (!arr[$0]++) {printf("%s%s",!ln++?"":":",$0)}}'`

btw ตรวจสอบให้แน่ใจว่าไม่มีไดเรกทอรีที่มี ":" ในเส้นทางของคุณมิฉะนั้นจะถูกทำให้ยุ่งเหยิง

ให้เครดิตกับ:


-1 สิ่งนี้ไม่ทำงาน ฉันยังคงเห็นรายการที่ซ้ำกันในเส้นทางของฉัน
dogbane

4
@dogbane: ลบรายการที่ซ้ำกันสำหรับฉัน อย่างไรก็ตามมันมีปัญหาที่ลึกซึ้ง เอาท์พุทมี: ในตอนท้ายซึ่งถ้าตั้งเป็น $ PATH ของคุณหมายความว่าไดเรกทอรีปัจจุบันจะเพิ่มเส้นทาง สิ่งนี้มีผลกระทบด้านความปลอดภัยในเครื่องที่มีผู้ใช้หลายคน
camh

@dogbane ใช้งานได้และฉันแก้ไขโพสต์เพื่อให้มีหนึ่งบรรทัดคำสั่งโดยไม่ต้องตาม:
akostadinov

@dogbane โซลูชันของคุณมีส่วนท้าย: ในผลลัพธ์
akostadinov

อืมงานคำสั่งที่สามของคุณ echo -nแต่สองคนแรกไม่ทำงานจนกว่าฉันจะใช้ ดูเหมือนว่าคำสั่งของคุณจะไม่ทำงานกับ "here strings" เช่นลอง:awk -v RS=: -v ORS=: '!arr[$0]++' <<< ".:/foo/bin:/bar/bin:/foo/bin"
dogbane

6

นี่คือซับหนึ่ง AWK

$ PATH=$(printf %s "$PATH" \
     | awk -vRS=: -vORS= '!a[$0]++ {if (NR>1) printf(":"); printf("%s", $0) }' )

ที่อยู่:

  • printf %s "$PATH" พิมพ์เนื้อหาของ $PATHโดยไม่ขึ้นบรรทัดใหม่
  • RS=: เปลี่ยนอักขระตัวคั่นเรคคอร์ดอินพุต (ค่าดีฟอลต์คือ newline)
  • ORS= เปลี่ยนตัวคั่นเร็กคอร์ดเอาต์พุตเป็นสตริงว่าง
  • a ชื่อของอาร์เรย์ที่สร้างขึ้นโดยนัย
  • $0 อ้างอิงระเบียนปัจจุบัน
  • a[$0] คือการอ้างอิงอาเรย์แบบเชื่อมโยง
  • ++ เป็นตัวดำเนินการภายหลังการเพิ่ม
  • !a[$0]++ ปกป้องด้านขวามือนั่นคือตรวจสอบให้แน่ใจว่ามีการพิมพ์ระเบียนปัจจุบันเท่านั้นหากไม่ได้พิมพ์มาก่อน
  • NR หมายเลขระเบียนปัจจุบันเริ่มต้นด้วย 1

ซึ่งหมายความว่า AWK จะใช้ในการแบ่งPATHเนื้อหาตาม:ตัวคั่นและเพื่อกรองรายการที่ซ้ำกันโดยไม่ต้องแก้ไขคำสั่ง

เนื่องจากอาร์เรย์เชื่อมโยงของ AWK ถูกนำไปใช้งานเป็นตารางแฮชรันไทม์จึงเป็นแบบเส้นตรง (เช่นใน O (n))

โปรดทราบว่าเราไม่จำเป็นต้องมองหา:ตัวละครที่ยกมาเพราะเปลือกหอยไม่ได้ให้การอ้างถึงการสนับสนุนไดเรกทอรีที่มี:ชื่อในPATHตัวแปร

อ๊ะ + แปะ

ข้างต้นสามารถทำให้ง่ายขึ้นด้วยการวาง:

$ PATH=$(printf %s "$PATH" | awk -vRS=: '!a[$0]++' | paste -s -d:)

pasteคำสั่งที่ใช้ในการกระจายผลผลิต awk กับทวิภาค สิ่งนี้ทำให้การดำเนินการ awk ง่ายต่อการพิมพ์ (ซึ่งเป็นการกระทำเริ่มต้น)

หลาม

เหมือนกับ Python สองซับ:

$ PATH=$(python3 -c 'import os; from collections import OrderedDict; \
    l=os.environ["PATH"].split(":"); print(":".join(OrderedDict.fromkeys(l)))' )

โอเค แต่สิ่งนี้จะลบสิ่งที่เป็นพิษออกจากสตริงที่มีการคั่นด้วยโคลอนที่มีอยู่หรือไม่หรือจะป้องกันไม่ให้มีการเพิ่ม dupe ในสตริงได้หรือไม่
Alexander Mills

1
ดูเหมือนอดีต
Alexander Mills

2
@AlexanderMills ดี OP เพิ่งถามเกี่ยวกับการลบรายการที่ซ้ำกันดังนั้นนี่คือสิ่งที่การโทร awk ทำ
maxschlepzig

1
pasteคำสั่งไม่ทำงานสำหรับฉันจนกว่าฉันเพิ่มต่อท้าย-จะใช้ STDIN
wisbucky

2
นอกจากนี้ฉันต้องเพิ่มช่องว่างหลังจากที่-vอื่นหรือฉันได้รับข้อผิดพลาด -v RS=: -v ORS=. awkไวยากรณ์ที่แตกต่างกันเพียงรสชาติ
wisbucky

4

ได้มีการอภิปรายที่คล้ายกันเกี่ยวกับเรื่องนี้ที่นี่

ฉันใช้แนวทางที่ต่างออกไปเล็กน้อย แทนที่จะยอมรับเส้นทางที่ตั้งจากไฟล์เริ่มต้นที่แตกต่างกันทั้งหมดที่ได้รับการติดตั้งฉันชอบใช้getconfเพื่อระบุเส้นทางของระบบและวางไว้ก่อนแล้วเพิ่มลำดับเส้นทางที่ต้องการจากนั้นใช้awkเพื่อลบรายการที่ซ้ำกัน สิ่งนี้อาจหรือไม่อาจเร่งความเร็วในการประมวลผลคำสั่ง (และในทางทฤษฎีแล้วจะปลอดภัยมากขึ้น) แต่มันทำให้ฉันรู้สึกอบอุ่น

# I am entering my preferred PATH order here because it gets set,
# appended, reset, appended again and ends up in such a jumbled order.
# The duplicates get removed, preserving my preferred order.
#
PATH=$(command -p getconf PATH):/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
# Remove duplicates
PATH="$(printf "%s" "${PATH}" | /usr/bin/awk -v RS=: -v ORS=: '!($0 in a) {a[$0]; print}')"
export PATH

[~]$ echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/lib64/ccache:/usr/games:/home/me/bin

3
นี้เป็นอันตรายมากเพราะคุณเพิ่มต่อท้าย:ไปPATH(เช่นรายการสตริงที่ว่างเปล่า) PATHแล้วเพราะไดเรกทอรีการทำงานปัจจุบันเป็นส่วนหนึ่งของคุณ
maxschlepzig

3

ตราบใดที่เราเพิ่มผู้บุกรุกที่ไม่ใช่ awk:

PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")

(อาจเป็นเรื่องง่ายเหมือนPATH=$(zsh -fc 'typeset -U path; echo $PATH')แต่ zsh จะอ่านอย่างน้อยหนึ่งzshenvไฟล์กำหนดค่าซึ่งสามารถแก้ไขPATHได้)

ใช้สองคุณสมบัติ zsh ที่ดี:

  • เซนต์คิตส์และเนวิสผูกกับอาร์เรย์ ( typeset -T)
  • และอาร์เรย์ที่ autoremove ซ้ำค่า ( typeset -U)

ดี! คำตอบการทำงานที่สั้นที่สุดและกำเนิดโดยไม่มีลำไส้ใหญ่ในตอนท้าย
Jaap

2
PATH=`perl -e 'print join ":", grep {!$h{$_}++} split ":", $ENV{PATH}'`
export PATH

สิ่งนี้ใช้ Perl และมีประโยชน์หลายประการ:

  1. มันจะลบรายการที่ซ้ำกัน
  2. มันทำให้การเรียงลำดับ
  3. มันทำให้การปรากฏตัวเร็วที่สุด ( /usr/bin:/sbin:/usr/binจะส่งผลให้/usr/bin:/sbin)

2

นอกจากนี้sed(ที่นี่ใช้sedไวยากรณ์GNU ) สามารถทำงาน:

MYPATH=$(printf '%s\n' "$MYPATH" | sed ':b;s/:\([^:]*\)\(:.*\):\1/:\1\2/;tb')

อันนี้ใช้ได้ดีเฉพาะในกรณีที่เส้นทางแรกเป็น.เหมือนตัวอย่างของ dogbane

ในกรณีทั่วไปคุณต้องเพิ่มsคำสั่งอื่น:

MYPATH=$(printf '%s\n' "$MYPATH" | sed ':b;s/:\([^:]*\)\(:.*\):\1/:\1\2/;tb;s/^\([^:]*\)\(:.*\):\1/:\1\2/')

มันทำงานได้แม้ในการก่อสร้างดังกล่าว:

$ echo "/bin:.:/foo/bar/bin:/usr/bin:/foo/bar/bin:/foo/bar/bin:/bar/bin:/usr/bin:/bin" \
| sed ':b;s/:\([^:]*\)\(:.*\):\1/:\1\2/;tb;s/^\([^:]*\)\(:.*\):\1/\1\2/'

/bin:.:/foo/bar/bin:/usr/bin:/bar/bin

2

ในขณะที่คนอื่น ๆ แสดงให้เห็นว่ามันเป็นไปได้ในหนึ่งบรรทัดโดยใช้ awk, sed, perl, zsh หรือ bash ขึ้นอยู่กับความอดทนของคุณสำหรับบรรทัดยาวและการอ่าน นี่คือฟังก์ชั่นทุบตีที่

  • ลบรายการที่ซ้ำกัน
  • แยมสั่ง
  • อนุญาตให้มีช่องว่างในชื่อไดเรกทอรี
  • ช่วยให้คุณระบุตัวคั่น (ค่าเริ่มต้นเป็น ':')
  • สามารถใช้กับตัวแปรอื่น ๆ ไม่ใช่แค่ PATH
  • ทำงานใน bash เวอร์ชัน <4 สำคัญถ้าคุณใช้ OS X ซึ่งสำหรับปัญหาเรื่องลิขสิทธิ์ไม่ได้จัดส่ง bash เวอร์ชัน 4

ฟังก์ชั่นทุบตี

remove_dups() {
    local D=${2:-:} path= dir=
    while IFS= read -d$D dir; do
        [[ $path$D =~ .*$D$dir$D.* ]] || path+="$D$dir"
    done <<< "$1$D"
    printf %s "${path#$D}"
}

การใช้

วิธีลบ dups ออกจาก PATH

PATH=$(remove_dups "$PATH")

1

นี่คือรุ่นของฉัน:

path_no_dup () 
{ 
    local IFS=: p=();

    while read -r; do
        p+=("$REPLY");
    done < <(sort -u <(read -ra arr <<< "$1" && printf '%s\n' "${arr[@]}"));

    # Do whatever you like with "${p[*]}"
    echo "${p[*]}"
}

การใช้งาน: path_no_dup "$PATH"

ตัวอย่างผลลัพธ์:

rany$ v='a:a:a:b:b:b:c:c:c:a:a:a:b:c:a'; path_no_dup "$v"
a:b:c
rany$

1

เวอร์ชันทุบตีล่าสุด (> = 4) รวมถึงอาร์เรย์ที่เชื่อมโยงกันนั่นคือคุณสามารถใช้ 'หนึ่งซับ' สำหรับทุบตีได้:

PATH=$(IFS=:; set -f; declare -A a; NR=0; for i in $PATH; do NR=$((NR+1)); \
       if [ \! ${a[$i]+_} ]; then if [ $NR -gt 1 ]; then echo -n ':'; fi; \
                                  echo -n $i; a[$i]=1; fi; done)

ที่อยู่:

  • IFS เปลี่ยนตัวคั่นฟิลด์อินพุตเป็น :
  • declare -A ประกาศอาร์เรย์ที่เชื่อมโยง
  • ${a[$i]+_}คือความหมายของการขยายพารามิเตอร์: _ถูกแทนที่หากa[$i]มีการตั้งค่าเท่านั้น ซึ่งคล้ายกับ${parameter:+word}การทดสอบที่ไม่เป็นโมฆะ ดังนั้นในการประเมินผลตามเงื่อนไขดังต่อไปนี้นิพจน์_(เช่นสตริงอักขระเดียว) จะประเมินค่าเป็นจริง (นี่เทียบเท่ากับ-n _) - ในขณะที่นิพจน์ว่างจะประเมินว่าเป็นเท็จ

+1: รูปแบบสคริปต์ที่ดี แต่คุณสามารถอธิบายไวยากรณ์เฉพาะได้${a[$i]+_}โดยแก้ไขคำตอบและเพิ่มหัวข้อย่อย ที่เหลือเป็นที่เข้าใจได้อย่างสมบูรณ์แบบ แต่คุณทำให้ฉันไปที่นั่น ขอขอบคุณ.
Cbhihe

1
@Chihhe ฉันได้เพิ่มสัญลักษณ์แสดงหัวข้อที่เน้นการขยายตัวนี้
maxschlepzig

ขอบคุณมาก. น่าสนใจมาก. ผมไม่ได้คิดว่าเป็นไปได้กับอาร์เรย์ (ไม่ใช่สตริง) ...
Cbhihe

1
PATH=`awk -F: '{for (i=1;i<=NF;i++) { if ( !x[$i]++ ) printf("%s:",$i); }}' <<< "$PATH"`

คำอธิบายของรหัส awk:

  1. แยกอินพุตด้วยโคลอน
  2. ผนวกรายการเส้นทางใหม่เข้ากับอาร์เรย์ที่เชื่อมโยงเพื่อค้นหาซ้ำอย่างรวดเร็ว
  3. พิมพ์อาเรย์แบบเชื่อมโยง

นอกเหนือจากความรัดกุมแล้วสายการบินหนึ่งยังเร็ว: awk ใช้แฮชตารางเพื่อให้ได้ประสิทธิภาพ O (1) ที่ตัดจำหน่ายแล้ว

ขึ้นอยู่กับการลบรายการ $ PATH ที่ซ้ำกัน


โพสต์เก่า แต่คุณช่วยอธิบายif ( !x[$i]++ )ได้ไหม. ขอบคุณ
Cbhihe

0

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

นี่คือตัวอย่าง:

$ MYPATH=.:/foo/bar/bin:/usr/bin:/foo/bar/bin
$ awk -F: '{for(i=1;i<=NF;i++) if(!($i in arr)){arr[$i];printf s$i;s=":"}}' <<< "$MYPATH"
.:/foo/bar/bin:/usr/bin

(อัปเดตเพื่อลบส่วนท้าย:)


0

วิธีแก้ปัญหา - ไม่ใช่แบบที่สวยงามเทียบเท่ากับที่เปลี่ยนแปลงตัวแปร * RS แต่อาจชัดเจนพอสมควร:

PATH=`awk 'BEGIN {np="";split(ENVIRON["PATH"],p,":"); for(x=0;x<length(p);x++) {  pe=p[x]; if(e[pe] != "") continue; e[pe] = pe; if(np != "") np=np ":"; np=np pe}} END { print np }' /dev/null`

โปรแกรมทั้งหมดทำงานในบล็อกBEGINและEND มันดึงตัวแปร PATH ของคุณจากสภาพแวดล้อมโดยแยกออกเป็นหน่วยต่างๆ จากนั้นจะวนซ้ำไปยังอาร์เรย์p ที่เกิด(ซึ่งถูกสร้างตามลำดับโดยsplit()) อาเรย์eเป็นอาเรย์แบบเชื่อมโยงที่ใช้เพื่อกำหนดว่าเราได้เห็นองค์ประกอบพา ธ ปัจจุบัน (เช่น/ usr / local / bin ) มาก่อนหรือไม่และจะผนวกเข้ากับnpด้วยตรรกะเพื่อผนวกโคลอนไปที่npหากมีข้อความเป็นnpอยู่แล้ว ENDบล็อกเพียง Echos NP สิ่งนี้สามารถทำให้ง่ายขึ้นโดยการเพิ่ม-F:ตั้งค่าสถานะกำจัดอาร์กิวเมนต์ที่สามเป็นsplit()(เป็นค่าเริ่มต้นเพื่อFS ) และเปลี่ยนnp = np ":"เป็นnp = np FSให้เรา:

awk -F: 'BEGIN {np="";split(ENVIRON["PATH"],p); for(x=0;x<length(p);x++) {  pe=p[x]; if(e[pe] != "") continue; e[pe] = pe; if(np != "") np=np FS; np=np pe}} END { print np }' /dev/null

อย่างไร้เดียงสาฉันเชื่อว่าfor(element in array)จะรักษาความสงบเรียบร้อย แต่มันก็ไม่ได้ดังนั้นทางออกดั้งเดิมของฉันใช้งานไม่ได้เนื่องจากคนจะรู้สึกไม่สบายใจหากมีคนตะกายออกคำสั่งของพวกเขาในทันที$PATH:

awk 'BEGIN {np="";split(ENVIRON["PATH"],p,":"); for(x in p) { pe=p[x]; if(e[pe] != "") continue; e[pe] = pe; if(np != "") np=np ":"; np=np pe}} END { print np }' /dev/null

0
export PATH=$(echo -n "$PATH" | awk -v RS=':' '(!a[$0]++){if(b++)printf(RS);printf($0)}')

เฉพาะเหตุการณ์แรกเท่านั้นที่จะถูกเก็บไว้และลำดับที่สัมพันธ์กันจะได้รับการดูแลอย่างดี


-1

ฉันจะทำมันด้วยเครื่องมือพื้นฐานเช่น tr, sort และ uniq:

NEW_PATH=`echo $PATH | tr ':' '\n' | sort | uniq | tr '\n' ':'`

หากไม่มีอะไรพิเศษหรือแปลกในเส้นทางของคุณมันควรจะทำงาน


BTW คุณสามารถใช้แทนsort -u sort | uniq
เร่ง

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