In reStructuredText, this doesn't work (the |reg| substitution isn't done):
.. include:: <isonum.txt>
foo bar|reg| baz
================
This does work, but I don't want a space between 'bar' and the |reg|:
.. include:: <isonum.txt>
foo bar |reg| baz
=================
How can I get substitutions recognized when they are immediately adjacent to a word?
This seems to work:
foo bar\ |reg| baz
Escaping the space with a backslash.
Related
What controls the environment to know to split by space in zsh?
I'm sure it's something simple but in all my searching have yet to figure it out what controls it.
Trying to loop over items in a space-separated string like so:
s='foo bar baz'
for i in $s; do
echo "$i END"
done
# foo bar baz END
# ---
s='foo bar baz'
a=( $s )
echo ${a[0]} # (empty)
echo ${a[1]} # foo bar baz
# ---
s='foo bar baz'
IFS=' ' read a <<< $s
for i in "${a[#]}"; do
echo "$i END"
done
# foo bar baz END
The different methods work via sh and bash, but in a shell with oh-my-zsh I'm unable to separate by space, getting the results above. May not be oh-my-zsh - but looking to understand what drives this.
Working example from bash:
s='foo bar baz'
for i in $s; do
echo "$i END"
done
# foo END
# bar END
# baz END
Zsh and bash are two different programming languages. They're similar, but not identical. In bash, and more generally in Bourne-style shells (sh, dash, ksh, …), an unquoted variable expansion $foo does the following:
Take the value of the variable foo, which is a string. (If there is no variable foo, take the empty string.)
Split the string into whitespace-separated parts. (More generally, the value of the IFS variable determines how the string is split; I won't go into all the details here.) The result is a list of strings.
For every element in the list, if it is a globbing pattern, i.e. if it contains at least one wildcard character *?\[ (and possibly more depending on some shell options), and that pattern matches at least one file name, then the element is replaced by the list of matching file names. Elements that don't contain any wildcard character, and elements that contain a wildcard character but don't match any file name, are left alone. The result is again a list of strings.
Zsh is mostly a Bourne-style shell, but it has some differences, and this is the main one: $foo has the following, simpler behavior.
Take the value of the variable foo, which is a string. (If there is no variable foo, take the empty string.)
If this results in an empty word, this word is eliminated. (So for example $foo$bar is only eliminated if both foo and bar are empty or unset.)
Note that in sh or bash, $foo only works to split a string if it doesn't contain any wildcard character or if globbing is disabled with set -f.
To split a string at whitespace in zsh, there are two simple methods:
Use the = parameter expansion specified to apply IFS word splitting. For example $=foo splits at whitespace as determined by IFS.
Use the p parameter expansion flag. For example ${(p: :)foo} splits at spaces (not tabs or newlines).
This has nothing to do with oh-my-zsh, which is a plugin to configure zsh for interactive use.
Just ${(p: :)foo} didn't work for me, and was giving a a zsh: error in flags error. After reading Parameter-Expansion doc, I see that it should be ${(ps: :)foo}. Even the flag p explanation in the doc uses the additional s flag.
The p flag doc says:
p :
Recognize the same escape sequences as the print builtin
in string arguments to any of the flags described below
that follow this argument.
So what I ended up using was just ${(s: :)foo}. See the example for the behavior where only space is used as the separator, contiguous spaces are treated as one, while tabs and newlines are preserved as is:
> FRUITS="apple\tbanana grapes orange passion_fruit\nwatermelon"
> for F in ${(ps: :)FRUITS}; do echo "GOT: <$F>"; done
GOT: <apple banana>
GOT: <grapes>
GOT: <orange>
GOT: <passion_fruit
watermelon>
I am trying to follow Make zsh complete arguments from a file but with a command. Shouldn't matter much.
In /usr/local/share/zsh/site-functions/_foo I have
#compdef foo
aliases=($(a complicated command here printing everything twice))
_describe aliases aliases
The command when ran alone prints this:
foo foo bar bar this this that that
This seems to create an associative array just fine. When I add echo ${(kv)aliases} before the _describe aliases aliases command and run /usr/local/share/zsh/site-functions/_foo I am getting
foo foo bar bar this this that that
And when I just do ${(k)aliases} then
foo bar this that
I do not really a description so this would work for me. But, it doesn't work for zsh.
I added
function foo() { echo $* }
autoload _foo
compdef _foo foo
to ~/.zshrc and after . ~/.zshrc when I type foo [tab] I get _foo:2: bad set of key/value pairs for associative array. I tried changing the command to only print everything once. Same results. I tried changing the command to print "foo:x " for every foo. Same results. So what about should my program produce for this to work?
aliases already exists as an associative array containing all your shell aliases. Call the variable something else.
It may alias help to declare your variable local, for a normal array:
local -a compl_aliases
The bad set of key/value pairs usually indicates that you have an odd number of elements when doing an associative array assignment. There needs to be an even number with a value for each key.
Let's take for example this file textfile.txt :
foo
bar
foo
bar
foo**word1**bar
foo
bar**word2**foo
foo
foo
bar
foo**word1**bar
foo
foo
bar**word2**foo
foo
foo
bar
foo**word1**bar
foo
bar**word2**foo
foo
bar
foo**word1**bar
foo
bar
foo
bar
bar**word2**foo
foo
What I am trying to do is : Search for a first word in a file, here the word is **word1**, and if this word has been found, search in the same line and the next two the second word, here it's **word2**
I tried to use grep to search the **word1**, with the -n option to get the line number. Then with this line number, extract with sed the matching line and the next two, and then do an other grep to search for the **word2**. It also should match each time **word1** and **word2**.
But it doesn't feel like it's the best way to achieve this.
In this example, there should be 3 positive matches : the last one doesn't work because **word2** is 4 lines ahead from **word1**, and I want a maximum of 2 lines ahead.
Concerning awk's output, I would like to output the line numbers where the two words matched, and also their respective lines where they have been found.
I also have a shell script returning output. What I would like to do is : for each matching couple words, print "my_script_result" + "awk_result" > file
this awk one-liner may help:
awk '/word1/{ok=1}ok && /word2/{print NR,$0}' file
In above line, /word1/ is your first word, /word2/ is your second word. The output would be matched line numbers and the matched lines.
It works in this way:
The script reads lines from the beginning of file, once word1 was found, set variable ok =1 (true). The 2nd part check ok AND word2 matched, if satisfied, print the output. Thus, if word2 was matched before we found word1, ok is false, the line will be skipped.
edit according to OP's update:
awk /word1/{ok=1;s=NR}ok && NR<=s+2 && /word2/{print NR,$0}' file
7 bar**word2**foo
20 bar**word2**foo
Choosing sed from the tagged tools:
echo shelloutput && sed -En "/word1/{/word2/{=;p;};N;/word2/{=;p;};N;s/^.*\n//;/word2/{=;p;};N;s/^.*\n//;/word2/{=;p;}}" EgrepToy.txt
Output:
shelloutput
7
bar**word2**foo
14
bar**word2**foo
20
bar**word2**foo
Works like this:
create some output echo shelloutput
continue directly to sed &&
Look for the first word /word1/{
look for second word /word2/{
conditionally print line number and found line =;p;};
fetch next line N;
delete first pattern space line, including newline, without terminating s/^.*\n//;
look for second word /word2/{
print line number =;
print matching line p;
literally repeat that twice
If you want two matches, i.e. only two following lines scanned for word2, then only repeat once, simply by deleting one N;s/^.*\n//;/word2/{=;p;};.
Choosing grep from the tagged tools:
echo shelloutput && grep -nA2 "word1" EgrepToy.txt | egrep "word2"
Output:
shelloutput
7-bar**word2**foo
20-bar**word2**foo
Since I am not sure whether I correclty understood "In this example, there should be 3 positive matches" (I think OP and I are somehow counting the "next lines" differently), I add an alternative to get three:
echo shelloutput && grep -nA3 "word1" EgrepToy.txt | egrep "word2"
Output:
shelloutput
7-bar**word2**foo
14-bar**word2**foo
20-bar**word2**foo
Both solutions work basically identically:
create desired shelloutput echo shelloutput
continue immediatly to grep &&
grep for the first word egrep word1
including the right number of following lines in the output -A2
adding the input file line number -n
grep the result for the second word | egrep word2
Echoing shelloutput is a placeholder for anything you want to do.
I have two files, both contain tens of thousands of lines. I'm currently taking a string (Z1234562) from file_one.txt and trying to see if its on file_two.txt. If it's found on file_two.txt, I'm returning the line number the match was on -- in this case, line 1235. I have this working already.
file_one.txt
Line 1> [...]_Z1234562
file_two.txt
Line 1234> [...],Z1234561,[...]
Line 1235> [...],Z1234562,[...]
Line 1236> [...],Z1234563,[...]
However, I want to now append to line 1235 the string ,Yes. So that on file_two.txt I have
Line 1235> [...],Z1234562,[...],Yes
With the help Glenn Jackman's answer of this other question I was able to figure out how to add text using the ed editor before and after a specific line within a file. However, I haven't been able to figure out if with ed I can add text to the end of a line within a file. Reading the documentation I'm not sure there is. So far, based off this AIX site, this is what I have:
(echo '15022a'; echo 'Text to add.'; echo '.'; echo 'w'; echo 'q') | ed -s filename.txt
This appends the string Text to add. after line 15,022. I'm wondering if there is an insert equivalent to this append.
The reason I'm not using sed is because I'm on an AIX system and I can't seem to get what this forum has working. However, I'm not sure if the sed command in this forum only solves adding text before or after a line and not at the end of the line, which I already have working.
My next approach is to remove the return character at the end of the line I want to append to, append, and then re-add the return character but I don't want to reinvent the wheel before exhausting my options. Maybe I'm missing something as I want to do this as soon as possible but any help would be appreciated. I'm starting not to like these AIX systems. Maybe awk can help me but I'm less familiar with awk.
I wrote a small binary search subroutine using Perl in order to find the line that I want to append to. I'm not sure if sed, ed, grep, awk, etc. use binary search but that's why I'm not using ed or sed's pattern-replace searches. I want it to be as fast as possible so I'm open to a different approach.
Here is a general recipe I have used for invoking ed from a script:
(echo '/pattern/s/$/ new text/'
echo w ) | ed filename
This invokes ed on filename, searches for a line containing pattern, and appends "new text" at the end of that line. Season to taste.
You said you were having trouble with sed, but for the record, here's the same sort of thing using sed:
sed '/pattern/s/$/ new text/' filename > filename.modified
You can use the j command
(.,.+1)j
Joins the addressed lines. The addressed lines are deleted from the buffer and replaced by a single line containing their joined text. The current address is set to the resultant line.
So you just have to modifiy your previous command :
cat << EOF | ed -s filename.txt
15022a
Text to add.
.
-1,.j
wq
EOF
First we create a test file foo:
$ cat > foo
foo
bar
If you already know the line number you want to edit, e.g. previous example and line number 2 using sed:
$ sed '2s/$/foo' foo
foo
barfoo
In awk you'd command:
$ awk 'NR==2 {sub(/$/,"foo")} 1' foo
foo
barfoo
In perl:
$ perl -p -e 's/$/foo/ if $. == 2' foo
foo
barfoo
Is there something similar to $1, but that gathers all input from the terminal input, including whitespace characters? This would be used to collect a pasted directory path that may have whitespaces - I need the whole string.
Thanks In Advance
Thankfully, I've received the answer to my first question. In execution, however, I can't get it to work. Here is my code. Can anyone explain what I'm doing wrong? Thanks.
alias finder='cd $* && open .'
It's returning segmented returns - every time it hits a space, it treats it as a separate entry.
Try $* or $#.
$* All of the positional parameters, seen as a single word
$# Same as $*, but each parameter is a quoted string, that is, the
parameters are passed on intact, without interpretation or expansion.
Normally you'd just refer to the first argument as "$1", including the quotation marks. If you want to use a directory name as an argument, and the name has spaces in it, you'd typically quote it on the command line:
alias finder='cd "$1" && open .'
...
finder "/some/dir/with spaces/in its name"
That also works well with tab completion, which escapes whitespace for you. And in this particular case, you probably might as well use the open command directly.
But if you want the finder alias to concatenate multiple arguments into a single string, separated by spaces, that actually turns out to be harder. I've tried some possibilities using $* and $#, but they don't work correctly. For testing, I'm using my own command echol, which prints each of its arguments on a separate line.
$ echol foo bar
foo
bar
$ alias e='echol "$*"'
$ e foo bar
foo
bar
$ alias e='eval echo \""$*"\"'
$ e foo bar
foo bar
That last one is the closest I've come, but it adds an extra leading space.
I think you're better off just quoting the directory name.