I have written a backup script that uses expect to automate rsync.
To make sure all files get backed up, I use rsync's --rsync-path="sudo rsync" option.
#!/bin/bash
set -e
expect <<- DONE
spawn rsync --rsync-path="sudo\\ rsync" -uav myuser#example.com:/home/myuser/ /backups/home/myuser
expect ":"
send -- "mypassword\r"
expect eof
DONE
This does not work as intended. I get the following error message:
bash: sudo rsync: command not found
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: error in rsync protocol data stream (code 12) at io.c(226) [Receiver=3.1.1]
I have seen similar questions with respect to using spaces in a rsync command line, and have added single quotes, double quotes, and escape backslashes here and there, but nothing has worked yet.
How do I make "--rsync-path with spaces" work within an expect script?
The problem is that you've got this:
--rsync-path="sudo\\ rsync"
Inside Expect/Tcl, this is seen as:
--rsync-path="sudo rsync"
And, because Tcl's quoting rules are not the same as bash's, that then uses "sudo rsync" with the double quotes as the command to send to the remote side. Which confuses things terribly. The correct fix is to omit the double quotes; the (backslash-quoted) backslash will ensure that it all gets into spawn as one argument, and gets sent to the other side correctly.
I really don't like using HEREdocs with Tcl. Too many things can go weird when different sorts of quoting interact. It's much better to use a single script in the real target language, since then you can use variables to make things clearer:
#!/usr/bin/env expect
set remoteRsync "sudo rsync"
set from myuser#example.com:/home/myuser/
set to /backups/home/myuser
set pass "mypassword"
spawn rsync --rsync-path=$remoteRsync -uav $from $to
expect ":"
send -- "$pass\r"
expect eof
exit
This makes the structure of the code much simpler to see, and easier to debug. The bit with /usr/bin/env at the start is just a way to avoid having the bash wrapper.
And no, those variables won't need quoting at use. Tcl is not bash.
You can't use --rsync-path to do that, since what you want is word-splitting, i.e. something the shell does.
So how can you run a command that runs a command by specifying a single pathname?
On the remote system, write a script wrapper susync doing the sudo (don't forget to chmod 755):
#!/bin/sh
exec /path/to/sudo /path/to/rsync "$#"
and use
spawn rsync --rsync-path=/path/to/susync ...
Related
Within tclsh I can run the following and get the expected output:
% exec bash -c "ulimit -v"
50331648
However within a Tcl script nothing is returned. No error, no output, nothing. There's clearly some gotcha with exec'ing 'bash -c' that I can't work out.
Alternatively, is there a native way in Tcl that I can get the system's memory limit to avoid having to do it this way in the first place?
In an interactive tclsh session, the REPL helpfully prints the output of commands/expressions. That's not the case in a non-interactive program.
exec returns the output of the command: you just need to capture it with the usual command substitution:
set output [exec bash -c "ulimit -v"]
puts $output
The code that you wrote should work; I can't identify why bash would silently fail to run ulimit -v. Even if the script was running in an environment where that was privileged information (why!?) one would still expect to get an error message of some form. That's a very weird problem!
Tcl's base command set doesn't expose any access to memory limits, whether for reading or writing. The simplest workaround that doesn't call an external program is the tclbsd package (apparently it mostly works on most other Unixes as well), which exposes a command that should help:
package require BSD
set limit [bsd::rlimit get soft virtual]
How can I embed a command like mysql -sN -e "query;" inside a perl script so that my shell will be the one making the database connection. The query is a little complicated query which runs perfectly from the command line but when I set it up in my perl program, i get a bunch of errors like "string found where operator expected" , "bareword found where operator expected" and syntax error. Any idea how to do this without making giving perl access to the database?
You can use back quotes, the example below will place the output of the OS ls -l command into content. Assuming you know the risks of what you are doing.
#!/usr/bin/perl
use strict;
my $content=`ls -l`;
print $content;
Is it possible to run some interactive shell from inside tclsh? Obviously it's easy to run an interactive shell such as bash or tclsh from inside bash, but I have not found a way to do the reverse.
If I run "exec tclsh" or "exec bash" from inside tclsh I don't get a prompt until I type "exit" and hit enter, or I use "ctrl-C" which kills the parent tclsh.
I would prefer not to use an external package, if at all possible.
Provided you don't want to pass values (other than the exit code) back to the calling Tcl code you can do it pretty easily by redirecting the standard channels so that Tcl doesn't capture them:
exec tclsh <#stdin >#stdout 2>#stderr
This will work for pretty much any subprocess (I've just tested it with vi) and is what tclsh actually does magically for you in interactive mode if it decides to try running a subprocess.
If you want to do anything more complex than that, you probably need to look into using Expect as there's a very long list of tricky gotchas otherwise.
I'm working on a project that require me to use window task scheduler to execute mysql query, this is the batch file content:
mysql -ufoo -pbar -D %1 < %2
when I tested the batch file via cmd:
task_sheduler.bat dbName pathToSqlFile
I get:
mysql -ufoo -pbar -D dbName 0<pathToSqlFile
I just want to say that its working, my question is what is about the extra space and the 0, where did they came from?
The extra space is between the dbName and 0
I'm using Windows 7 Ultimate Service Pack 1 (x64)
I assume your Batch file does NOT have an #echo off command, so you refer to the echo of commands that appear in the screen when a Batch file is executed. The display of these commands frequently include additional characters that cmd.exe inserts to display exactly the executed commands.
In the case of redirections, <input is a short form of Stdin redirection, and the number of Stdin is zero, so the real redirection is 0<input. The same happens with >output, that is echoed as 1>output. cmd.exe also remove multiple spaces from the original code and insert needed ones in order to clearly show the executed commands.
If you want not to see these command expantions, just insert an #echo off command at beginning of your Batch file.
cmd prefixes all redirection commands by the default handle if none is provided. The handles are defined here. 0<file thus means that we want file to be redirected to standard input. The extra space is there to prevent a command like hi.exe<myfile from being wrongly interpreted as hi.exe0 < myfile
I'm having a rather strange problem with zsh. When I start up my shell, everything - functions, environment vars, aliases, etc. - all work fine. I've created the following function and sourced it in zsh:
clean()
{
path=/tmp
for i in ${path}/*; do
echo $i
done
}
Running clean in the terminal works as expected, in that it prints out all the files in /tmp/. Afterward, however, trying any command - for example, ls - produces this:
zsh: command not found: ls
I have several other functions that work just fine, which leads me to believe that somehow, that loop is causing the problem. At any rate, this is very frustrating and I would sincerely appreciate the community's eyes. Thanks!
The problem is assigning the path variable - since zsh has that variable reserved (in addition to PATH), overwriting it removes the ability for the shell to find any command.
The correct answer, of course, is to use a variable other than $path:
local_path=/tmp
for i in ${local_path}/*; do
echo $i
done
I guess it overwrites the variable path, which is the one used to find commands. That's why it doesn't find commands anymore.
I dugg for hours to find this again. Zsh offer a nice trick to remove duplicates from PATH-type variables, a bit out of topic, but may be useful :
typeset -T PYTHONPATH pythonpath
typeset -U pythonpath
the first line bind PYTHONPATH colon separated string to zsh array
the second remove duplicates