ฉันจะสร้าง "คำสั่งเชลล์" ของตัวเองได้อย่างไร (เช่นคำสั่ง mkdir / cd)


41

ฉันไม่สามารถบอกได้ว่ากี่ครั้งที่ฉันปรารถนาคำสั่งที่จะสร้างไดเรกทอรีและย้ายไปยังไดเรกทอรีนั้น โดยทั่วไปฉันต้องการเทียบเท่าต่อไปนี้:

mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path

แต่ต้องพิมพ์/arbitrarily/long/pathครั้งเดียวเท่านั้น:

mk-cd /arbitrarily/long/path

ฉันพยายามสร้างสคริปต์เพื่อทำสิ่งนี้ แต่เปลี่ยนเฉพาะไดเรกทอรีภายในสคริปต์ ฉันต้องการไดเรกทอรีในเปลือกที่มีการเปลี่ยนแปลงเช่นกัน

#!/bin/bash
mkdir $1
cd $1
export PWD=$PWD

ฉันจะทำงานนี้ได้อย่างไร


12
ด้วยcdคุณเลือกกรณีพิเศษตั้งแต่เริ่มต้น : D
Daniel B

2
ไม่ตรงกับที่ฉันกำลังค้นหา แต่cdข้อมูลที่เกี่ยวข้องสุดยอด(กลับไปยังไดเรกทอรีก่อนหน้าโดยcd -ใช้ใช้pushdและpopdเพื่อรักษา "สแต็ค" ของไดเรกทอรี): superuser.com/questions/324512/ …
Bott Bottoms

8
คุณสามารถพิมพ์mkdir -p /very/long/pathจากนั้นใช้cdเว้นวรรคแล้วกด Alt + .เพื่อทำซ้ำอาร์กิวเมนต์สุดท้ายเช่นชื่อ dir
choroba

2
ไม่ได้คำตอบเพียงแค่แสดงความคิดเห็นคล้ายกับ @ choroba: mkdir -p /very/long/path; cd !#:2คุณยังสามารถใช้ สตริง!#:2จะขยายเป็นอาร์กิวเมนต์ nr 2 (นั่นคืออาร์กิวเมนต์ที่สาม/very/long/pathเนื่องจากการนับเริ่มต้นด้วยศูนย์)
Ingo Blechschmidt

3
@IngoBlechschmidt !$เพียงง่ายยิ่งขึ้นเนื่องจากเป็นอาร์กิวเมนต์สุดท้ายของคำสั่งสุดท้ายที่คุณสามารถใช้ ผมใช้เคล็ดลับนี้โดยเฉพาะตลอดเวลาแม้ว่าจะมีจำนวนมากเป็นมากกว่าที่คุณสามารถทำอะไรกับการขยายตัวของประวัติศาสตร์
สัญลักษณ์แทน

คำตอบ:


92

วิธีที่ง่ายที่สุดคือการใช้ฟังก์ชันเชลล์:

mkcd() {
    mkdir -p -- "$1" && cd -- "$1"
}

วางไว้ใน.bashrcไฟล์ของคุณเพื่อให้พร้อมใช้งานสำหรับคุณเช่นเดียวกับคำสั่งเชลล์อื่น

สาเหตุที่มันไม่ทำงานเป็นสคริปต์ภายนอกคือcdการเปลี่ยนไดเรกทอรีปัจจุบันของสคริปต์ที่กำลังทำงาน แต่ไม่มีผลกับการเรียก นี่คือโดยการออกแบบ! แต่ละกระบวนการมีไดเร็คทอรี่การทำงานของตัวเองซึ่งสืบทอดโดยลูก ๆ ของมัน แต่สิ่งที่ตรงกันข้ามไม่สามารถทำได้

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

การ&&ใช้ที่นี่เพื่อแยกทั้งสองคำสั่งที่ใช้หมายถึงหากคำสั่งแรกสำเร็จ ( mkdir) ให้รันคำสั่งที่สอง ( cd) ดังนั้นหากmkdirล้มเหลวในการสร้างไดเรกทอรีที่ร้องขอไม่มีจุดใดพยายามเข้าไปได้ ข้อความแสดงข้อผิดพลาดถูกพิมพ์โดยmkdirและนั่นคือมัน

-pตัวเลือกที่ใช้กับmkdirจะมีการบอกยูทิลิตี้นี้เพื่อสร้างไดเรกทอรีใด ๆ ที่หายไปเป็นส่วนหนึ่งของเส้นทางแบบเต็มของชื่อไดเรกทอรีเป็นอาร์กิวเมนต์ ผลข้างเคียงอย่างหนึ่งคือถ้าคุณขอให้สร้างไดเรกทอรีที่มีอยู่แล้วmkcdฟังก์ชั่นจะไม่ล้มเหลวและคุณจะสิ้นสุดในไดเรกทอรีนั้น ที่อาจถูกพิจารณาว่าเป็นปัญหาหรือคุณสมบัติ ในกรณีก่อนหน้านี้ฟังก์ชั่นสามารถปรับเปลี่ยนได้เช่นวิธีที่เพียงแค่เตือนผู้ใช้:

mkcd() {
    if [ -d "$1" ]; then
        printf "mkcd: warning, \"%s\" already exists\n" "$1"
    else
        mkdir -p "$1" 
    fi && cd "$1"
}

หากไม่มี-pตัวเลือกพฤติกรรมของฟังก์ชันเริ่มต้นจะแตกต่างกันมาก

หากไดเรกทอรีที่มีไดเรกทอรีที่จะสร้างนั้นไม่มีอยู่mkdirฟังก์ชันก็จะล้มเหลว

หากไดเรกทอรีที่จะสร้างมีอยู่แล้วmkdirล้มเหลวด้วยและcdจะไม่ถูกเรียก

ท้ายที่สุดโปรดทราบว่าการตั้งค่า / ส่งออกPWDนั้นไม่มีประโยชน์เนื่องจากเชลล์ดำเนินการภายในแล้ว

แก้ไข: ฉันเพิ่ม--ตัวเลือกให้กับทั้งสองคำสั่งสำหรับฟังก์ชั่นเพื่ออนุญาตชื่อไดเรกทอรีที่ขึ้นต้นด้วยเส้นประ


5
คุณควรเพิ่มว่าคำสั่งนี้จะไม่สามารถใช้งานได้อย่างถาวรเว้นแต่จะถูกเพิ่มเข้าไป~/.bashrcหรือสคริปต์เริ่มต้นอย่างใดอย่างหนึ่ง
AFH

มีวิธีทุบตีเพื่อทำเทียบเท่ากับ tcsh alias mk-cd 'mkdir -p \!:1; cd \!:1'โดยไม่มีฟังก์ชั่นหรือไม่? หรือบางทีฉันควรเริ่มคิดฟังก์ชั่นทุบตีมากขึ้นเช่นนามแฝงขยาย?
โทมัส Padron-McCarthy

@ ThomasPadron-McCarthy การcshส่งผ่านข้อโต้แย้งนี้ไม่ได้นำไปใช้กับนามแฝงสไตล์ bourne ฟังก์ชั่นเป็นวิธีที่จะไปแน่นอนมีความยืดหยุ่นมากขึ้น
jlliagre

@ ThomasPadron-McCarthy - คุณอาจต้องการดูคำตอบนี้ซึ่งให้กลไกที่ค่อนข้างลึกซึ้งสำหรับการเข้าถึงพารามิเตอร์ที่ส่งผ่านไปยังนามแฝง (ในคำจำกัดความของbar) ตัวเลือกอื่นจะใช้history 1เช่นเดียวกับalias mk-cd='args="$(history 1)" && args="${args#*mk-cd\ }" && mkdir -p "$args" && cd'- วิธีนี้ใช้ได้ผลดีสำหรับตัวอย่างของผู้ถาม แต่ไม่ใช่วิธีแก้ปัญหาทั่วไปเนื่องจากคำสั่งอื่น ๆ ในบรรทัดเดียวกัน (เช่นการวางท่อเอาต์พุต) จะทำให้ล้มเหลว
AFH

3
@ ThomasPadron-McCarthy คุณควรเริ่มนึกถึงนามแฝงของ tcsh มากกว่าฟังก์ชั่นที่ถูก จำกัด
Gilles 'ดังนั้นหยุดความชั่วร้าย'

32

ฉันต้องการเพิ่มทางเลือกอื่น

สคริปต์ของคุณจะทำงานหากคุณเรียกใช้ด้วยคำสั่ง. หรือsource:

. mk-cd /arbitrarily/long/path

หากคุณทำเช่นนี้exportในสคริปต์ไม่จำเป็น คุณสามารถข้ามการพิมพ์.โดยใช้alias:

alias mk-cd='. mk-cd'

เหตุผลที่การทำงานนี้เป็นปกติสคริปต์จะทำงานใน sub-shell ดังนั้นสภาพแวดล้อมจะหายไปเมื่อเสร็จสิ้น แต่คำสั่ง.(หรือsource) บังคับให้มันรันในเชลล์ปัจจุบัน

เทคนิคนี้มีประโยชน์สำหรับลำดับของคำสั่งที่ซับซ้อนเกินไปสำหรับฟังก์ชั่นหรืออยู่ระหว่างการพัฒนาและแก้ไขบ่อยครั้ง: เมื่อaliasมีการป้อนเข้าไป.bash_aliasesคุณสามารถแก้ไขสคริปต์ได้โดยไม่ต้องกำหนดค่าเริ่มต้นใหม่

หมายเหตุดังต่อไปนี้: -

หากคุณเขียนสคริปต์ซึ่งจะต้องเรียกด้วยคำสั่ง./ sourceมีสองวิธีในการตรวจสอบให้แน่ใจนี้: -

  1. ด้วยเหตุผลบางอย่างสคริปต์ที่ถูกเรียกใช้ด้วย./ sourceไม่จำเป็นต้องถูกเรียกใช้งานดังนั้นเพียงแค่ลบการอนุญาตนี้และสคริปต์จะไม่ถูกพบใน "$ PATH" หรือจะให้ข้อผิดพลาดในการอนุญาตหากมีการเรียกด้วยเส้นทางที่ชัดเจน

  2. หรือจะใช้เคล็ดลับต่อไปนี้ที่ส่วนหัวของสคริปต์:

bind |& read && { echo 'Must be run from "." or "source" command'; exit 1; }

วิธีนี้ใช้งานได้เนื่องจากใน sub-shell bindมีการเตือนถึงแม้ว่าจะไม่มีสถานะข้อผิดพลาด: ดังนั้นreadจะใช้เพื่อให้ข้อผิดพลาดในการไม่มีอินพุต


.ขอบคุณสำหรับข้อมูลเกี่ยวกับ ฉันทำมาแล้ว. .bashrcนับครั้งไม่ถ้วน แต่ไม่รู้ว่า.มันทำอะไร มันคล้ายกับexport?
cst1992

2
@ cst1992 - ไม่คำสั่งแตกต่างไปจากเดิมอย่างสิ้นเชิง: export varคัดลอกตัวแปรภายในvarไปยังสภาพแวดล้อมซึ่งถูกสืบทอดและนำเข้าเป็นตัวแปรในเชลล์ย่อยใด ๆ แต่ไม่มีผลกับพาเรนต์เชลล์ โดยปกติแล้ว sub-shell นั้นถูกสร้างขึ้นเพื่อรันสคริปต์และการเปลี่ยนแปลงใด ๆ ที่มันทำ (รวมถึงตัวแปรที่ส่งออก) จะหายไปเมื่อมันเสร็จสมบูรณ์ เพื่อให้สคริปต์ตั้งค่าตัวแปรในเชลล์ได้ต้องรันในเชลล์นั้นและนี่คือสิ่งที่.คำสั่งทำ: รันสคริปต์โดยไม่สร้างเชลล์ย่อย อยากรู้อยากเห็นสคริปต์ที่เรียกใช้โดย.ไม่จำเป็นต้องมีสิทธิ์ในการปฏิบัติการ
AFH

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

@ cst1992 - ฉันตอบคำถามดั้งเดิม แต่ฉันไม่ต้องการที่จะถ่วงคำตอบของฉันเกี่ยวกับปัญหาเสริม หากคุณกำลังมองหาคำอธิบายของ.และexportคำสั่งชื่อของคำถามนี้จะไม่ทำให้คุณคาดหวังคำตอบที่นี่ดังนั้นฉันจะให้คำตอบของฉันยืนได้เหมือนเดิม แต่ขอบคุณสำหรับความคิดเห็นของคุณ
AFH

+1 โดยทั่วไป แต่โดยเฉพาะอย่างยิ่งสำหรับนามแฝง ฉันมีสคริปต์บางอย่างที่ต้องมีแหล่งที่มาและฉันมักจะลืมที่จะเรียกใช้วิธีเหล่านั้น นามแฝงดูแลอย่างนั้น
Joe

13

ไม่ใช่คำสั่งเดียว แต่คุณไม่จำเป็นต้องพิมพ์เส้นทางทั้งหมดอีกครั้งเพียงแค่ใช้!$(ซึ่งเป็นอาร์กิวเมนต์สุดท้ายจากคำสั่งก่อนหน้าในประวัติเชลล์)

ตัวอย่าง:

mkdir -p /arbitrarily/long/path
cd !$

1

โดยการสร้างชื่อแทน วิธีที่นิยมมากในการสร้างคำสั่งของคุณเอง

คุณสามารถสร้างนามแฝงได้ทันที:

alias myalias='mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path'

แค่นั้นแหละ. หากคุณต้องการรักษานามแฝงนั้นอย่างถาวร (ไม่ใช่เฉพาะเซสชันปัจจุบัน) ให้ใส่คำสั่งนั้นใน dotfile (โดยทั่วไปคือ ~ / .bash_aliases หรือ ~ / .bash_profile)


4
เนื่องจากชื่อไดเรกทอรีที่ยาวคือ hardcoded นามแฝงนี้จะไม่เป็นประโยชน์มากเว้นแต่ไดเรกทอรีนี้จะถูกทำลายโดยกระบวนการอื่น ๆ เป็นประจำ
jlliagre

1

นอกจากสิ่งที่ได้รับการแนะนำแล้วผมอยากจะแนะนำให้thefuck เป็นกรอบการทำงานของ Python สำหรับการปรับปรุงกระบวนการเชลล์ของคุณโดยแก้ไขข้อผิดพลาด ได้รับแรงบันดาลใจจากทวีตนี้สิ่งที่คุณต้องทำคือพิมพ์ชื่อแทนที่ตั้งค่าไว้ (โดยค่าเริ่มต้นfuck) แล้วมันจะพยายามแก้ไขคำสั่งก่อนหน้าของคุณ หนึ่งในการแก้ไขของมันทำงานดังนี้:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.