Chuyển đến thư mục bằng các biến bash không hoạt động khi tên thư mục có khoảng trắng


11

Giả sử tôi muốn lưu trữ lệnh sau trong một biến

cd "/cygdrive/c/Program Files/"

Vì vậy tôi làm điều này

dir="cd \"/cygdrive/c/Program Files/\""

Điều đó sẽ lưu trữ lệnh để điều hướng đến thư mục Tệp chương trình, vì vậy khi tôi gõ $ dir, nó sẽ đưa tôi đến thư mục đó. Để kiểm tra xem các trích dẫn đã được thoát đúng chưa, tôi gõ

echo $dir

mang lại cho tôi

cd "/cygdrive/c/Program Files/"

Vì vậy, mọi thứ nên được làm việc tốt. Tuy nhiên, khi tôi gõ,

$dir

tôi có

bash: cd: "/cygdrive/c/Program: No such file or directory

Tôi đang làm gì sai? Tôi đang sử dụng Cygwin, nhưng tôi cho rằng vấn đề này áp dụng cho bash nói chung.


Thay vào đó, hãy thử xóa các trích dẫn xung quanh tên thư mục và thoát dấu cách bằng dấu gạch chéo ngược.
Mike Fitzpatrick

Câu trả lời:


8

Câu trả lời ngắn: xem BashFAQ # 050 ("Tôi đang cố gắng đặt một lệnh vào một biến, nhưng các trường hợp phức tạp luôn thất bại!").

Câu trả lời dài: khi bash phân tích cú pháp một lệnh, nó phân tích cú pháp dấu ngoặc kép trước khi nó thay thế các biến; nó không bao giờ quay trở lại và phân tích lại các trích dẫn trong các giá trị của các biến, vì vậy chúng kết thúc không làm gì hữu ích. Sử dụng echo để kiểm tra lệnh hoàn toàn sai lệch vì nó hiển thị lệnh sau khi được phân tích cú pháp; nếu bạn muốn xem những gì thực sự được thực thi, hãy sử dụng set -xcác lệnh in shell khi chúng được thực thi hoặc sử dụng printf "%q " $dir; echothay vì echo đơn giản.

Nếu bạn muốn lưu trữ một lệnh phức tạp (tức là một lệnh có khoảng trắng hoặc các ký tự đặc biệt khác trong "words"), bạn cần đặt nó trong một mảng thay vì một biến văn bản đơn giản, sau đó mở rộng nó với thành ngữ `" $ { mảng [@]} ", như thế này:

dir=(cd "/cygdrive/c/Program Files/")
"${dir[@]}"

Bây giờ, nếu mục đích là để tạo ra một tốc ký dễ gõ, thì đây rõ ràng không phải là cách để đi. Sử dụng bí danh shell hoặc hàm thay thế:

alias dir='cd "/cygdrive/c/Program Files/"'
dir

hoặc là

dir() { cd "/cygdrive/c/Program Files/"; }
dir

7

Khi bash mở rộng $dir, nó chỉ thực hiện tách từ và tạo khối trên nó. Lời chia tách tạo ra ba chữ: cd, "/cygdrive/c/ProgramFiles/". Sau đó, lệnh để thực hiện là cdvới hai đối số; cdchỉ nhìn vào đối số đầu tiên của nó "/cygdrive/c/Program, không phải là một thư mục hiện có.

Nếu bạn muốn thực hiện đánh giá toàn bộ vỏ trên nội dung của một biến, hãy sử dụng eval:

eval "$dir"

Lưu ý rằng bạn cần dấu ngoặc kép xung quanh $dir, nếu không, việc tách từ sẽ được thực hiện trước tiên, sau đó evalsẽ nối các đối số của nó với khoảng trắng. Điều này sẽ xảy ra để làm việc ở đây, nhưng nói chung sẽ trở nên tồi tệ (ví dụ nếu có hai khoảng trắng liên tiếp trong một tên tệp).

Tuy nhiên, một chuỗi không phải là cách đúng để lưu trữ lệnh shell mà bạn muốn thực thi. Trừ khi bạn có một yêu cầu bất thường, bạn nên sử dụng một chức năng thay thế:

dir () {
  cd "/cygdrive/c/Program Files/"
}

Nếu bạn thực sự quan tâm đến việc gõ $dirđể chuyển sang một thư mục cụ thể và đây không chỉ là một ví dụ đơn giản, vì bash 4, hãy đặt và đặt shopt -s autocdcủa bạn.bashrc

dir="/cygdrive/c/Program Files/"

sau đó bạn có thể gõ chỉ "/cygdrive/c/Program Files/"hoặc "$dir"tại dấu nhắc shell để chuyển sang thư mục đó. Bạn vẫn cần các trích dẫn kép xung quanh $dir; nếu bạn không thích điều đó, hãy sử dụng zsh thay vì bash.


6

Lưu trữ các lệnh trong các biến nói chung không phải là một ý tưởng tốt. Đó là những chức năng và bí danh dành cho.

Thay vì

dir="cd \"/cygdrive/c/Program Files/\""

hãy thử một trong những điều sau:

dir="/cygdrive/c/Program Files"
...
cd "$dir"

hoặc là

dir() { cd "/cygdrive/c/Program Files"; }
dir

hoặc là

alias dir='cd "/tmp/Program Files"'
...
dir

Biểu mẫu đầu tiên, lưu trữ tên của thư mục trong một biến, có nghĩa là gõ nhiều hơn một chút, nhưng sẽ rõ ràng hơn khi bạn thực hiện lệnh mà bạn đang thực hiện cd. Thứ hai là gần nhất với những gì bạn đang cố gắng thực hiện. (Tôi không sử dụng bí danh nhiều trong bash; Tôi thực sự không chắc họ có lợi thế gì so với các chức năng.)

BIÊN TẬP :

dircó lẽ là một tên xấu cho một bí danh hoặc hàm, vì đã có một lệnh hiện có tên đó (về cơ bản nó là một phiên bản ls, ít nhất là nếu bạn có GNU coreutils).


upvote choStoring commands in variables is generally not a good idea. That's what functions and aliases are for.
Rob

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.