1 |
Am 27.02.2011 22:06, schrieb Alex Schuster: |
2 |
> Florian Philipp writes: |
3 |
> |
4 |
>> I'm currently streamlining some of my shell scripts to avoid unnecessary |
5 |
>> process calls where bash itself is powerful enough. |
6 |
>> |
7 |
>> At the moment, I want to replace stuff like this: |
8 |
>> string='foo:bar:foo' |
9 |
>> second_field=$(echo $string | cut -d : -f 2) # should read "bar" |
10 |
>> |
11 |
>> My current solution is using two string operations: |
12 |
>> string='foo:bar:foo' |
13 |
>> # remove everything up to and including first ':' |
14 |
>> second_and_following=${string#*:} |
15 |
>> # remove everything from the first ':' following |
16 |
>> second_field=${second_and_following%%:*} |
17 |
> |
18 |
> That's how I do these things, too. |
19 |
> |
20 |
>> Of course, I normally do this in a single line with a subshell but it |
21 |
> |
22 |
> Hmm, I don't get this. Subshell? |
23 |
|
24 |
second_field=$(second_and_following=${string#*:}; echo |
25 |
${second_and_following%%:*}) |
26 |
|
27 |
Putting it in parentheses creates a subshell. New variables don't leave |
28 |
the scope. I guess I should have said command substitution but both |
29 |
concepts apply here. |
30 |
|
31 |
> |
32 |
>> still looks cumbersome. Is there a way to do it in a single operation |
33 |
>> without a temporary variable? The following does not work: |
34 |
>> string='foo:bar:foo' |
35 |
>> second_field=${string#:%%:*} |
36 |
> |
37 |
> I don't think so. But you can write a shell function for this: |
38 |
> |
39 |
> getfield() |
40 |
> { |
41 |
> local str=${1#*:} |
42 |
> echo "${str%%:*} |
43 |
> } |
44 |
> |
45 |
> string='foo:bar:foo' |
46 |
> second_field=$( getfield "$string" ) |
47 |
> |
48 |
> But if you need to do this very often in a loop, sometimes going back to |
49 |
> cut can speed things up, when you place it outside: |
50 |
> |
51 |
> See |
52 |
> |
53 |
> for string in $( < inputfile ) |
54 |
> do |
55 |
> second_field=$( getfield "$string" ) |
56 |
> do_something_with $second_field |
57 |
> done |
58 |
> |
59 |
> vs. |
60 |
> |
61 |
> secondfields=( $( cut -d : -f 2 inputfile ) ) |
62 |
> for secondfield in ${secondfields[@]} |
63 |
> do |
64 |
> do_something_with $second_field |
65 |
> done |
66 |
> |
67 |
> So, in th e2nd example, cut is called only once, but processes all input |
68 |
> lines. The result is put into an array. |
69 |
> |
70 |
|
71 |
Agreed. Using one long pipe (when applicable) is the real strength of |
72 |
shell programming. |
73 |
|
74 |
> Of course, all stuff should be put into double quotes in case there is |
75 |
> whitescape involved. Setting IFS to $'\n' may be necessary for this when |
76 |
> creating the array. |
77 |
> |
78 |
> Wonko |
79 |
> |
80 |
|
81 |
Agreed, again. I removed the quotes from my examples to improve |
82 |
readability. It was already bad enough with all those regular expressions. |
83 |
|
84 |
Thanks for the input. |
85 |
|
86 |
Florian Philipp |