I am using Python's Fabric to execute some tasks on remote hosts through ssh.
One of the tasks uses carriage return character in its output, when displaying progress-bars of sub-tasks. Something similar to this:
int main(int argc, char* argv[]) {
int i;
for (i = 0; i <= 5; ++i) {
printf("\rProgress: %d%%", (int)(100 * ((float)i / 5)));
fflush(stdout);
sleep(1);
}
printf("\n");
return 0;
}
When this runs with python's Fabric:
def some_task():
with settings(user = 'root'):
run('./task_with_progress_bar.out')
tasks.execute(some_task, hosts = ['server_name'])
Fabric will ignore the \r
, and instead of having the progress bar in one line, what I'll get is:
[server_name] Executing task 'some_task'
[server_name] run: ./task_with_progress_bar.out
[server_name] out:
[server_name] out: Progress: 0%
[server_name] out: Progress: 20%
[server_name] out: Progress: 40%
[server_name] out: Progress: 60%
[server_name] out: Progress: 80%
[server_name] out: Progress: 100%
[server_name] out:
It seems like \r
is being treated as \n
...
It the example above it's not too bad, but the actual task I am running is filled with progress bars that progress with smaller steps, and this pollutes the entire console with unnecessary output.
Is there a way to somehow customize the output so that Fabric will not ignore the \r
, but still keep the same general format of: [<server_name>] <action_being_taken>
? I like this format, it's neat and clean.
The problem is that fabric is transforming \r
in \n
.
A workaround to this problem is to create a file like class and pass it as stdout
argument to run
, so we can process the output produced by the called program and translate \n
back to \r
. The challenge of writing such class is to identify which \n
must be translated to \r
. Here is attempt that looks for "Progress:"
to identify such cases:
class FixProgress:
def __init__(self):
self.saw_progress = False
def write(self, s):
if 'Progress:' in s:
self.saw_progress = True
if s == '\n' and self.saw_progress:
sys.stdout.write('\r')
self.saw_progress = False
else:
sys.stdout.write(s)
def flush(self):
sys.stdout.flush()
def some_task():
with settings(user = 'root'):
run('./task_with_progress_bar.out', stdout = FixProgress())
This worked correctly with the c program in the question.