It's a bit weird, but it is documented:
-k
[num]
(…) Input is read from the terminal unless one of -u
or -p
is present.
The reason your first attempt hangs there is that it's reading from the terminal. Typing three characters on the terminal does unblock it. To read from standard input when you're asking for a limited number of characters rather than a whole line (with -k
or -q
), you need to pass -u 0
explicitly.
echo foobar | ( read -u 0 -k 3; echo $REPLY )
You need to use "$(somecmd "$file")"
.
Without the quotes, a path with a space will be split in the argument to somecmd
, and it will target the wrong file. So you need quotes on the inside.
Any spaces in the output of somecmd
will also cause splitting, so you need quotes on the outside of the whole command substitution.
Quotes inside the command substitution have no effect on the quotes outside of it. Bash's own reference manual isn't too clear on this, but BashGuide explicitly mentions it. The text in POSIX also requires it, as "any valid shell script" is allowed inside $(...)
:
With the $(command)
form, all characters following the open parenthesis to the matching closing parenthesis constitute the command. Any valid shell script can be used for command, except a script consisting solely of redirections which produces unspecified results.
Example:
$ file="./space here/foo"
a. No quotes, dirname
processes both ./space
and here/foo
:
$ printf "<%s>\n" $(dirname $file)
<.>
<here>
b. Quotes inside, dirname
processes ./space here/foo
, giving ./space here
, which is split in two:
$ printf "<%s>\n" $(dirname "$file")
<./space>
<here>
c. Quotes outside, dirname
processes both ./space
and here/foo
, outputs on separate lines, but now the two lines form a single argument:
$ printf "<%s>\n" "$(dirname $file)"
<.
here>
d. Quotes both inside and outside, this gives the correct answer:
$ printf "<%s>\n" "$(dirname "$file")"
<./space here>
(that would possibly have been simpler if dirname
only processed the first argument, but that wouldn't show the difference between cases a and c.)
Note that with dirname
(and possibly others) you also need want to add --
, to prevent the filename from being taken as an option in case it happens to start with a dash, so use "$(dirname -- "$file")"
.
Best Answer
Zsh has a built-in way to sort lists. However, I don't think there's a way to sort the values while keeping the correlation with the keys using parameter expansion flags and subscript flags, which means that an explicit loop is necessary. Assuming that your values don't contain a null character, you can build an array containing the values and keys concatenated with a null character in between, and sort that.
EDIT by @sch: the first 4 lines can be simplified to
The variables
keys
andvalues
contain the keys and values ofA
in an arbitrary but consistent order. You can writekeys=(${(k)A})
if there are no empty keys, and similarly for values.keys_sorted_by_decreasing_value
sorts keys lexicographically, add then
flag to sort numerically (9
before10
) and removeO
if you want to sort in increasing order (in which case the top two values can be obtained with the subscript[-2,-1]
).Ksh93 has a way to sort the positional parameters only, with
set -s
; this also exists in zsh but not in bash 4.2. Assuming your values don't contain newlines or control characters that sort before newlines:This is all pretty complex, so you might as well go for the external sort, which is a lot easier to write. Assuming that neither keys nor values contain control characters, in ksh or bash: