Line continuation in command substitution - tcl

Given this short code snippet:
set a [list \
foo \
bar
]
When I remove the \ after list I get invalid command name "foo" and when I remove the \ after foo I get invalid command name "bar".
However, the code as I have put it runs fine and I do not get something like invalid command name "]".
Why is no \ required after bar?

The contents of a […] sequence is actually a script, not just a command (though people mostly don't take advantage of this because they're not crazy). Putting extra blank lines at the end of the script doesn't change what the result of the script is: it's the result of the last command in the script (unless the script is empty, when it is the empty string).
Leaving the \ characters out means that there's a multi-command script in there, which is legal (though you've not created the commands foo or bar). Having them in means that you've got an overall script that is equivalent to:
set a [list foo bar
]
With exactly those spaces. That's usually practically identical to this:
set a [list foo bar]
But the difference is there.

There is no error message because the recursive evaluation ends at the close bracket. It's not part of the command substitution script. As far as the recursive evaluation in the command substitution is concerned, that's just a list command with some white space following it.

Related

Printing variables including functions from Makefile and/or variable introspection

If you iterate over .VARIABLES and print each, any true variable can be printed correctly with the following rule:
print_variables: $(foreach V,$(.VARIABLES),print-$(V)) .phony_explicit
print-%: .phony_explicit; #echo "$* = \"$($*)\""
.PHONY: .phony_explicit ...
A 0- or 1-line function will still work, but any more will result in Syntax error: Unterminated quote string. Just one multiline function will break the entire print_variables rule. As a workaround, I have added ;\ to each line in my function definitions, but that won't fix existing multiline functions (either via includes from this makefile or via other makefiles including this one.) What can I do? Is there a container of just function variables, or a way to test if a variable is a function definition?
A simple minimal example would be easier to understand; this has nothing to do with .VARIABLES, pattern rules, etc. (and I'm not sure what the point of the .phony_explicit prereq is..)
define F
foo
bar
endef
print: ; echo "F = $(F)"
will show the problem:
echo "F = foo
/bin/sh: 1: Syntax error: Unterminated quoted string
This is because when make sees a variable that contains newlines in a recipe, it assumes that the newlines mean you want the lines of the variable to become lines in the recipe.
First in general you should use single-quotes around strings you send to the shell, unless you need the shell to expand them; it won't help in this situation but in general it's much safer.
There's no way to undo that, really. You have a number of options.
The first is to not use echo but instead use the make function info:
print-F: ; $(info F = "$(F)")
yields:
F = "foo
bar"
Another option is to use subst to replace the newlines with some other value. The new value cannot itself contain explicit newlines, but you can ask the shell to print a newline for you:
# Create a variable containing a single newline
# Note this must contain TWO newlines!
define NL
endef
print-F: printf 'F = "$(subst %,%%,$(subst $(NL),\n,$(F))"\n'
Yields:
printf 'F = "foo\nbar"\n'
F = "foo
bar"
One final option is to convert your makefile to use the .ONESHELL feature, but I assume that's a step too far just to get this debugging output available :).

Expect: extract specific string from output

I am navigating a Java-based CLI menu on a remote machine with expect inside a bash script and I am trying to extract something from the output without leaving the expect session.
Expect command in my script is:
expect -c "
spawn ssh user#host
expect \"#\"
send \"java cli menu command here\r\"
expect \"java cli prompt\"
send \"java menu command\"
"
###I want to extract a specific string from the above output###
Expect output is:
Id Name
-------------------
abcd 12 John Smith
I want to extract abcd 12 from the above output into another expect variable for further use within the expect script. So that's the 3rd line, first field by using a double-space delimiter. The awk equivalent would be: awk -F ' ' 'NR==3 {$1}'
The big issue is that the environment through which I am navigating with Expect is, as I stated above, a Java CLI based menu so I can't just use awk or anything else that would be available from a bash shell.
Getting out from the Java menu, processing the output and then getting in again is not an option as the login process lasts for 15 seconds so I need to remain inside and extract what I need from the output using expect internal commands only.
You can use regexp in expect itself directly with the use of -re flag. Thanks to Donal on pointing out the single quote and double quote issues. I have given solution using both ways.
I have created a file with the content as follows,
Id Name
-------------------
abcd 12 John Smith
This is nothing but your java program's console output. I have tested this in my system with this. i.e. I just simulated your program's output with cat. You just replace the cat code with your program commands. Simple. :)
Double Quotes :
#!/bin/bash
expect -c "
spawn ssh user#domain
expect \"password\"
send \"mypassword\r\"
expect {\\\$} { puts matched_literal_dollar_sign}
send \"cat input_file\r\"; # Replace this code with your java program commands
expect -re {-\r\n(.*?)\s\s}
set output \$expect_out(1,string)
#puts \$expect_out(1,string)
puts \"Result : \$output\"
"
Single Quotes :
#!/bin/bash
expect -c '
spawn ssh user#domain
expect "password"
send "mypasswordhere\r"
expect "\\\$" { puts matched_literal_dollar_sign}
send "cat input_file\r"; # Replace this code with your java program commands
expect -re {-\r\n(.*?)\s\s}
set output $expect_out(1,string)
#puts $expect_out(1,string)
puts "Result : $output"
'
As you can see, I have used {-\r\n(.*?)\s\s}. Here the braces prevent any variable substitutions. In your output, we have a 2nd line with full of hyphens. Then a newline. Then your 3rd line content. Let's decode the regex used.
-\r\n is to match one literal hyphen and a new line together. This will match the last hyphen in the 2nd line and the newline which in turn make it to 3rd line now. So, .*? will match the required output (i.e. abcd 12) till it encounters double space which is matched by \s\s.
You might be wondering why I need parenthesis which is used to get the sub-match patterns.
In general, expect will save the expect's whole match string in expect_out(0,string) and buffer all the matched/unmatched input to expect_out(buffer). Each sub match will be saved in subsequent numbering of string such as expect_out(1,string), expect_out(2,string) and so on.
As Donal pointed out, it is better to use single quote's approach since it looks less messy. :)
It is not required to escape the \r with the backslash in case of double quotes.
Update :
I have changed the regexp from -\r\n(\w+\s+\w+)\s\s to -\r\n(.*?)\s\s.
With this way - your requirement - such as match any number of letters and single spaces until you encounter first occurrence of double spaces in the output
Now, let's come to your question. You have mentioned that you have tried -\r\n(\w+)\s\s. But, there is a problem here with \w+. Remember \w+ will not match space character. Your output has some spaces in it till double spaces.
The use of regexp will matter based on your requirements on the input string which is going to get matched. You can customize the regular expressions based on your needs.
Update version 2 :
What is the significance of .*?. If you ask separately, I am going to repeat what you commented. In regular expressions, * is a greedy operator and ? is our life saver. Let us consider the string as
Stackoverflow is already overflowing with number of users.
Now, see the effect of the regular expression .*flow as below.
* matches any number of characters. More precisely, it matches the longest string possible while still allowing the pattern itself to match. So, due to this, .* in the pattern matched the characters Stackoverflow is already over and flow in pattern matched the text flow in the string.
Now, in order to prevent the .* to match only up to the first occurrence of the string flow, we are adding the ? to it. It will help the pattern to behave as non-greedy manner.
Now, again coming back to your question. If we have used .*\s\s, then it will match the whole line since it is trying to match as much as possible. This is common behavior of regular expressions.
Update version 3:
Have your code in the following way.
x=$(expect -c "
spawn ssh user#host
expect \"password\"
send \"password\r\"
expect {\\\$} { puts matched_literal_dollar_sign}
send \"cat input\r\"
expect -re {-\r\n(.*?)\s\s}
if {![info exists expect_out(1,string)]} {
puts \"Match did not happen :(\"
exit 1
}
set output \$expect_out(1,string)
#puts \$expect_out(1,string)
puts \"Result : \$output\"
")
y=$?
# $x now contains the output from the 'expect' command, and $y contains the
# exit status
echo $x
echo $y;
If the flow happened properly, then exit code will have value as 0. Else, it will have 1. With this way, you can check the return value in bash script.
Have a look at here to know about the info exists command.

Tcl: Is parameter evaluation guaranteed to be left-to-right?

I have a Tcl program where I often find expressions of the following kind:
proc func {} {...}
...
lappend arr([set v [func]]) $v
The intended meaning of the last line is
set v [func]
lappend arr($v) $v
It obviously works. What I would like to know: Does it work "by accident", or does Tcl guarantee, that the first parameter passed to lappend is evaluated before the second?
Tcl is always evaluated from left to right as you can read on the documentation, I quote the part:
Substitutions take place from left to right, and each substitution is evaluated completely before attempting to evaluate the next. Thus, a sequence like:
set y [set x 0][incr x][incr x]
will always set the variable y to the value, 012.
Agreed with Jerry. Adding some flavor in it.
Tcl commands are evaluated in two steps : parsing & execution.
First the Tcl interpreter parses the command string into words, performing substitutions along the way.
Then a command procedure processes the words to produce a result string. Each command has a separate command procedure.
Let us consider the following code.
%set input "The cat in the hat"
The cat in the hat
%string match "*at in*" $input
1
In the parsing step the Tcl interpreter applies the rules described in this chapter to divide the command up into words and perform substitutions.
Parsing is done in exactly the same way for every command. During the parsing step the Tcl interpreter does not apply any meaning to the values of the words. Tcl just performs a set of simple string operations such as replacing the characters $a with the string stored in variable a. Tcl does not know or care whether a or the resulting word is a number or the name of a widget or anything else.
In the execution step meaning is applied to the words of the command. Tcl treats the first word as a command name, checking to see if the command is defined and locating a command procedure to carry out its function. If the command is defined then the Tcl interpreter invokes its command procedure, passing all of the words of the command to the command procedure. The command procedure is free to interpret the words in any way that it pleases, and different commands apply very different meanings to their arguments.
Major rule to remember here
Tcl parses a command and makes substitutions in a single pass from left to right. Each character is scanned exactly once.
At most a single layer of substitution occurs for each character; the result of one substitution is not scanned for further
substitutions.
Reference : Tcl and the Tk Toolkit

GNU make call function with multiple arguments and multiple commands

I am trying to write a GNU make call function (example below) which has multiple shell commands to execute, such that it can be called with different arguments.
shell_commands = $(shell echo $(1); ls -ltr $(2))
try:
$(call shell_commands,$(FILE1),$(FILE2))
1) Is above the correct way to write a call function with multiple commands? By using a semi-colon to separate them? To make it readable, I write my targets as shown below. Is there a similar way to write a call function?
shell_commands:
echo $(1)
ls -ltr $(2)
2) I get this error from make when I execute make -B try. It looks like it is trying to execute /home/user/file1. But why?
make: execvp: /home/user/file1: Permission denied
make: *** [try] Error 127
3) Is it possible to pass variable number of parameters to a call function? Like pass in just the second parameter and not the first one.
$(call shell_commands,,$(FILE2))
I tried google-ing, searching on SO, and looking on gnu.org but I did not get any solutions. Will appreciate any answers or pointers to any resources which document call function with multiple optional arguments and commands.
Question 1: No, this is not right. The shell make function should NEVER be used inside a recipe: the recipe is already running in the shell, so why would you run another shell? It's just confusing. Second, it's generally recommended to use && between multiple commands in a recipe, so that if the first command fails the entire command will immediately fail, rather than continuing on and perhaps succeeding. Of course, that is not always correct either, it depends on what you're trying to do.
Question 2: This happens because the shell make function is like backticks in the shell: it expands to the output printed by the shell command it runs. Your shell command that make runs is:
echo $(1); ls -ltr $(2)
(where, one assumes, $1 expands to /home/user/file1) which prints the string /home/user/file1. After the expansion, that string is substituted into the recipe and make tries to run that recipe, giving the error you see above.
You want this, most likely:
shell_commands = echo $(1) && ls -ltr $(2)
try:
$(call shell_commands,$(FILE1),$(FILE2))
Now the call expands to the actual text, not an invocation of make's shell function, and then that text is run as the recipe.
Question 3: Sure, just using empty parameters means that the variable $1 (in this case) expands to the empty string.

tmux: why do these two lines cause a ".tmux.conf:2: can't establish current session" on startup?

I have only these 2 lines in ~/.tmux.conf:
unbind r
bind r source-file ~/.tmux.conf; display "Reloaded"
I start up tmux with just
tmux
And
/Users/.../.tmux.conf:2: can't establish current session
is the result.
Why is this, and how can I prevent it?
You have a typo in the second command; you need to escape the semicolon. See the example in man tmux:
bind-key R source-file ~/.tmux.conf \; \
display-message "source-file done"
As the manual goes on to explain:
Multiple commands may be specified together as part of a command sequence. Each command should be separated by spaces and a semicolon; commands are
executed sequentially from left to right and lines ending with a backslash continue on to the next line, except when escaped by another backslash. A
literal semicolon may be included by escaping it with a backslash (for example, when specifying a command sequence to bind-key).
I just had this error message when I had a few set commands in there which didn't have the -g flag. So if someone has that problem, try adding -g to your sets.