Tôi có thể sử dụng một biến trong mở rộng cú đúp Bash không?


10

Dưới đây là một số loại mã giả cho những gì tôi đang cố gắng thực hiện:

#!/bin/bash

# I already have the variable below figured out (positive integer):
numlines=$([returns number of lines containing specific characters in a file])

# This is basically what I want to do with it:
for i in {1..$numlines}; do
    # the part below is already figured out as well:        
    do some other stuff
done

Tôi có thể thực hiện nó tốt từ dòng lệnh bằng cách chèn số thực vào chuỗi `{1..n} '. Tôi chỉ cần biết nếu có thể bao gồm một biến ở đây và làm thế nào để thực hiện nó.

  • Tôi đã thử exporting nó
  • Tôi đã thử đặt biến chính nó trong dấu ngoặc nhọn trong chuỗi: {1..${numlines}}
  • Tôi đã thử đặt nó trong dấu ngoặc kép với hy vọng nó sẽ mở rộng: {1.."$numlines"}
  • Tôi đã cố gắng thoát khỏi $:{1..\$numlines}

Tôi có cần sử dụng một set -[something]lệnh để biến này được mở rộng không? Tôi thậm chí đã thử một số hình thức sử dụng eval... tất cả đều vô ích.

Tôi chỉ cần biết nếu có một cái gì đó đơn giản hoặc tối nghĩa mà tôi đang thiếu hoặc nếu điều này thậm chí có thể trước khi tôi lãng phí thời gian nữa cho nó.

Tôi có thể kết hợp một cách thực sự, thực sự táo bạo để làm cho nó hoạt động khi cần thiết, nhưng tôi muốn tránh điều đó nếu có thể và học cách đúng đắn để thực hiện nó.


Câu trả lời:


11

Thật không may, không có cách nào để sử dụng một biến trong bản mở rộng đó (AFAIK), vì việc mở rộng biến xảy ra sau khi mở rộng dấu ngoặc.

May mắn thay, có một công cụ làm công việc tương tự.

for i in $(seq 1 $numlines); do
    # stuff
done

seqlà từ lõi GNU; không biết làm thế nào để làm điều đó trong POSIX.


1
(Cộng 1). seqlà tốt cho các hệ thống GNU và, nếu tôi nhớ chính xác, OSX gần đây nhất. Trên các hệ thống BSD khác, người ta có thể sử dụng jot thay thế.
John1024

seqhoạt động hoàn hảo. Cảm ơn bạn rất nhiều vì câu trả lời nhanh chóng của bạn.
rubynorails

Đây không phải là một phần của câu hỏi của tôi - nhưng cú pháp (nếu có) để làm điều này theo thứ tự ngược lại, chẳng hạn như là {16..1}gì? $(seq $numlines 1)đã không làm việc. Tôi đoán tôi có thể luôn luôn man seq, nhưng chỉ tự hỏi nếu có ai biết ra khỏi đỉnh đầu của họ.
rubynorails

1
Chỉ cần tìm ra cách làm ngược lại từ liên kết này -for i in $(seq $numlines -1 1)
rubynorails

seq ${numlines} -1 0
DopeGhoti

10

Chắc chắn rồi. Nếu bạn muốn một vòng lặp for tăng một biến số nguyên, hãy sử dụng dạng forvòng lặp tăng một biến số nguyên (hay nói chung là thực hiện số học trên (các) biến số vòng lặp).

for ((i=1; i<=numlines; i++)); do  done

Cấu trúc này hoạt động trong bash (và ksh93 và zsh), nhưng không phải trong sh đơn giản. Trong sh đơn giản, sử dụng vòng lặp while và cấu trúc test ( [ … ]).

i=1
while [ "$i" -le "$numlines" ]; do
  
  i=$((i+1))
done

8

Nếu bạn phải tránh seq, điều mà Tom Hunt chỉ ra dường như là giải pháp thông thường cho vấn đề này, thì evalchắc chắn có thể làm điều đó (mặc dù, tôi sẽ không khuyến khích điều đó):

eval 'for i in {1..'$numlines'}; do echo $i; done'

Bạn có thể duy trì POSIX bằng cách tránh mở rộng {} và chỉ cần thực hiện so sánh toán học và số nguyên trên $numlines:

while [ ! "$numlines" -eq 0 ]; do
     echo "$numlines"
     : $((numlines-=1))
done

Ngoài POSIX, bashkshzshcũng có C kiểu forvòng:

for((i=0; i<numlines; i++)); do echo $i; done

1
Tôi thực sự đánh giá cao câu trả lời này là tốt. Mặc dù seqhoạt động tốt cho kịch bản của tôi và dường như là giải pháp đơn giản nhất, thật tốt khi biết rằng có những lựa chọn khác (thậm chí là POSIX). Cám ơn vì cái này.
rubynorails

2
Thực sự không có lý do để sử dụng eval; nếu bạn có mở rộng cú đúp, bạn có vòng lặp kiểu C.
chepner

@PSkocik - Nếu tôi có thể chọn 2 câu trả lời, tôi cũng sẽ chọn câu trả lời này. Khi tôi bắt gặp thực tế là tôi cần phải làm điều này ngược lại, evalví dụ của bạn là đơn giản nhất và sẽ giúp tôi không phải tìm kiếm một cách khác để sử dụng nó seq. Các whilevòng lặp là một chút cồng kềnh đối với tôi. Tôi thích giữ mọi thứ ngắn gọn và ngọt ngào, và tôi không bao giờ có thể làm cho forvòng lặp kiểu C hoạt động bằng cách cho igiá trị 0 hoặc 1. Nó không bao giờ trả lại chính xác và luôn bị tắt. Tôi chắc chắn rằng nó có thể được điều chỉnh để hoạt động chính xác, nhưng dù sao đây cũng là những giải pháp hữu ích.
rubynorails

Cách evaltiếp cận là có vấn đề nếu có bất cứ điều gì không tầm thường trong cơ thể của vòng lặp. Tôi tưởng tượng nó sẽ không thể đọc được nếu bạn cần lồng hai vòng như vậy.
kasperd
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.