LLDB: print string with line breaks in a pretty fashion - lldb

In LLDB when I print a string containing line breaks it prints \n which messes up the entire output (especially for C++ string types). Is there a flag/option/whatever to tell LLDB to really print linebreaks instead of the escaped variants?

(lldb) setting set escape-non-printables false
Will do what you want - cfr.
(lldb) p #"Hello World\nYes I mean it"
(__NSCFString *) $0 = 0x0000000100b00180 #"Hello World
Yes I mean it"
vs. the default
(lldb) p #"Hello World\nYes I mean it"
(__NSCFString *) $1 = 0x0000000100b00000 #"Hello World\nYes I mean it"

Related

gdb in C: Get type of variable as string

I want to see if it is possible to get the type of a variable in gdb as a string. For example, if
int i = 1;
MyStruct *ms = NULL;
then I want to get something like
(gdb) <the-command-I-am-looking-for> i $local_var_i
(gdb) p $local_var_i
$local_var_i = "int"
(gdb) <the-command-I-am-looking-for> ms $local_var_ms
(gdb) p $local_var_ms
$local_var_ms = "MyStruct *"
I may have allocated the local variables before the above code segment, and the command may be a custom command.
Is such a thing possible? How could I achieve this?
Edit for clarification:
I have a group of functions in my program that change name according to the type they serve (I know it's not remotely the best way to do this, but I cannot change that). I want to write a gdb function which I can feed with just the variable and the rest will be done automatically, without my intevention. Preferably, I would also like to avoid a wall of if/else if.
If your gdb supports the python scripting extensions, you can try it like this:
define type2var
eval "python gdb.execute(\"set $%s=\\\"\"+(gdb.execute(\"ptype %s\", False, True)).strip()+\"\\\"\")",$arg1,$arg0
end
Now you can call it like this:
>>> type2var "variable" "typevar"
>>> print $typevar
$1 = "type = char [32]"
You can of course format it further as needed using python string functions.

How to portability use "${#:2}"?

On Allow for ${#:2} syntax in variable assignment they say I should not use "${#:2}" because it breaks things across different shells, and I should use "${*:2}" instead.
But using "${*:2}" instead of "${#:2}" is nonsense because doing "${#:2}" is not equivalent to "${*:2}" as the following example:
#!/bin/bash
check_args() {
echo "\$#=$#"
local counter=0
for var in "$#"
do
counter=$((counter+1));
printf "$counter. '$var', ";
done
printf "\\n\\n"
}
# setting arguments
set -- "space1 notspace" "space2 notspace" "lastargument"; counter=1
echo $counter': ---------------- "$*"'; counter=$((counter+1))
check_args "$*"
echo $counter': ---------------- "${*:2}"'; counter=$((counter+1))
check_args "${*:2}"
echo $counter': ---------------- "${#:2}"'; counter=$((counter+1))
check_args "${#:2}"
-->
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
1: ---------------- "$*"
$#=1
1. 'space1 notspace space2 notspace lastargument',
2: ---------------- "${*:2}"
$#=1
1. 'space2 notspace lastargument',
3: ---------------- "${#:2}"
$#=2
1. 'space2 notspace', 2. 'lastargument',
If I cannot use "${#:2}" (as they say), what is the equivalent can I use instead?
This is original question Process all arguments except the first one (in a bash script) and their only answer to keep arguments with spaces together is to use "${#:2}"
There's context that's not clear in the question unless you follow the links. It's concerning the following recommendation from shellcheck.net:
local _help_text="${#:2}"
^––SC2124 Assigning an array to a string! Assign as array, or use * instead of # to concatenate.
Short answer: Don't assign lists of things (like arguments) to plain variables, use an array instead.
Long answer: Generally, "${#:2}" will get all but the first argument, with each treated as a separate item ("word"). "${*:2}", on the other hand, produces a single item consisting of all but the first argument stuck together, separated by a space (or whatever the first character of $IFS is).
But in the specific case where you're assigning to a plain variable, the variable is only capable of storing a single item, so var="${#:2}" also collapses the arguments down to a single item, but it does it in a less consistent way than "${*:2}". In order to avoid this, use something that is capable of storing multiple items: an array. So:
Really bad: var="${#:2}"
Slightly less bad: var="${*:2}"
Much better: arrayvar=("${#:2}") (the parentheses make this an array)
Note: to get the elements of the array back, with each one treated properly as a separate item, use "${arrayvar[#]}". Also, arrays are not supported by all shells (notably, dash doesn't support them), so if you use them you should be sure to use a bash shebang (#!/bin/bash or #!/usr/bin/env bash). If you really need portability to other shells, things get much more complicated.
Neither ${#:2} nor ${*:2} is portable, and many shells will reject both as invalid syntax. If you want to process all arguments except the first, you should get rid of the first with a shift.
first="${1}"
shift
echo The arguments after the first are:
for x; do echo "$x"; done
At this point, the first argument is in "$first" and the positional parameters are shifted down one.
This demonstrates how to combine all ${#} arguments into a single variable one without the hack ${#:1} or ${#:2} (live example):
#!/bin/bash
function get_all_arguments_as_single_one_unquoted() {
single_argument="$(printf "%s " "${#}")";
printf "unquoted arguments %s: '%s'\\n" "${#}" "${single_argument}";
}
function get_all_arguments_as_single_one_quoted() {
single_argument="${1}";
printf "quoted arguments %s: '%s'\\n" "${#}" "${single_argument}";
}
function escape_arguments() {
escaped_arguments="$(printf '%q ' "${#}")";
get_all_arguments_as_single_one_quoted "${escaped_arguments}";
get_all_arguments_as_single_one_unquoted ${escaped_arguments};
}
set -- "first argument" "last argument";
escape_arguments "${#}";
-->
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
quoted arguments 1: 'first\ argument last\ argument '
unquoted arguments 4: 'first\ argument last\ argument '
As #William Pursell answer points out, if you would like to get only {#:2} arguments, you can add a shift call before "${#}"
function escape_arguments() {
shift;
escaped_arguments="$(printf '%q ' "${#}")";
get_all_arguments_as_single_one_quoted "${escaped_arguments}";
get_all_arguments_as_single_one_unquoted ${escaped_arguments};
}
-->
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
quoted arguments 1: 'last\ argument '
unquoted arguments 2: 'last\ argument '

splittling a file into multiple with a delimiter awk

I am trying to split files evenly in a number of chunks. This is my code:
awk '/*/ { delim++ } { file = sprintf("splits/audio%s.txt", int(delim /2)); print >> file; }' < input_file
my files looks like this:
"*/audio1.lab"
0 6200000 a
6200000 7600000 b
7600000 8200000 c
.
"*/audio2.lab"
0 6300000 a
6300000 8300000 w
8300000 8600000 e
8600000 10600000 d
.
It is giving me an error: awk: line 1: syntax error at or near *
I do not know enough about awk to understand this error. I tried escaping characters but still haven't been able to figure it out. I could write a script in python but I would like to learn how to do this in awk. Any awkers know what I am doing wrong?
Edit: I have 14021 files. I gave the first two as an example.
For one thing, your regular expression is illegal; '*' says to match the previous character 0 or more times, but there is no previous character.
It's not entirely clear what you're trying to do, but it looks like when you encounter a line with an asterisk you want to bump the file number. To match an asterisk, you'll need to escape it:
awk '/\*/ { close(file); delim++ } { file = sprintf("splits/audio%d.txt", int(delim /2)); print >> file; }' < input_file
Also note %d is the correct format character for decimal output from an int.
idk what all the other stuff around this question is about but to just split your input file into separate output files all you need is:
awk '/\*/{close(out); out="splits/audio"++c".txt"} {print > out}' file
Since "repetition" metacharacters like * or ? or + can take on a literal meaning when they are the first character in a regexp, the regexp /*/ will work just fine in some (e.g. gawk) but not all awks and since you apparently have a problem with having too many files open you must not be using gawk (which manages files for you) so you probably need to escape the * and close() each output file when you're done writing to it. No harm doing that and it makes the script portable to all awks.

Keep only last line of a repeated pattern

I would like to know if it is possible to delete all the lines of a selected pattern except the last one. It is not so easy to explain, so I will make an example.
I have a text file with content similar to this:
A sent (1)
A received (1)
B sent (1)
B sent (2)
B sent (3)
B received (1)
I would like to have an alternation between "sent" and "received" messages, where the "sent" one is the last between the sent messages with the same letter. So I need an output like:
A sent (1)
A received (1)
B sent (3)
B received (1)
Is there some program that can do something like that? I can use either Ubuntu or Windows, or build a simple C/C++ application, if necessary.
Here's a simple way:
tac FILE | uniq -w 6 | tac
We:
Reverse-print the file using tac (necessary for uniq to work right here).
Weed out duplicate lines basing uniqueness on only the first 6 characters (thereby ignoring the incrementing number in parantheses). Only the first line of a set of duplicate lines is kept, which is why we have used tac.
Then reverse-print the file again so it's in the order you want.
Under linux, this can be a one-liner, for example in awk:
awk '$1 $2 != prev {if (buf) print buf} {prev = $1 $2; buf = $0} END {print buf}' mylog.txt
The exact syntax depends on your pattern. Here, I just use the first two words ($1 $2) of the line to determine whether a line should be skipped. The skipped lines ($0) are stored in a temporary which is printed when the pattern is different or at the END.
If it is okay to print the first line of a similar block instead of the last line, the script reduces to:
awk '$1 $2 != prev; {prev = $1 $2}' mylog.txt
or you can use the even more succinct alternative:
uniq -w 6
which sorts out unique lines, but considering only the first 6 characters.
In C, something like this will do:
bool isFirstSent = false;
bool isSecondSent = false;
char *firstLine = getLine(file); // getline returns a char array
// if a line exists and NULL otherwise
if (strstr(firstLine, "sent"))
isFirstSent = true;
char *secondLine = getLine(file);
while (secondLine)
{
if (strstr(secondLine, "sent"))
isSecondSent = true;
else
isSecondSent = false;
if (isFirstSent != isSecondSent)
printf("%s", firstLine);
free(firstLine);
isFirstSent = isSecondSent;
firstLine = secondLine;
secondLine = getLine(file);
}
free(firstLine);

store gdb call expr return value

how to store return value of call expr in gdb? For example:
(gdb) call rbt_next(&some_global,0x0)
$2 = (rbt_node *) 0x7169ee00
rbt_next returns the pointer next node. However, when I try to do:
(gdb) set $w_sess = call rbt_next(&some_global,0x0)
Error: No symbol "call" in current context
I get the above error.
The reason why I want this is because I need to traverse a global rb tree to find a match. What I want to accomplish is this:-
while $w_sess = call rbt_next($some_global, $c_sess) != 0x0
if $c_sess == $w_sess
printf "Sess found in tree\n"
else
printf "Sess not found\n"
end
end
Leave out the word call. The part after set $foo = is just an ordinary expression like you'd use with print.

Resources