Thread
-
[PATCH v3 3/3] psql: Fix counting of header and footer lines in pager setup
Erik Wienhold <ewie@ewie.name> — 2025-09-09T00:36:49Z
* The table header only produces extra lines when printed in normal mode. So don't count those lines in tuples_only mode or expanded mode. * Count the lines of the table title, if present. * Count all footer lines instead of treating each footer as a single line. --- src/fe_utils/print.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c index 82f4fc9af20..94043fae663 100644 --- a/src/fe_utils/print.c +++ b/src/fe_utils/print.c @@ -3394,15 +3394,8 @@ IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap, NULL, &nl_lines, NULL); header_height[i] = nl_lines; - - if (nl_lines > max_lines) - max_lines = nl_lines; } - /* Add height of tallest header column */ - lines += max_lines; - max_lines = 0; - /* Scan all cells to count their lines */ for (i = 0, cell = cont->cells; *cell; cell++) { @@ -3449,12 +3442,34 @@ IsPagerNeeded(const printTableContent *cont, unsigned int *width_wrap, { printTableFooter *f; - /* - * FIXME -- this is slightly bogus: it counts the number of - * footers, not the number of lines in them. - */ + if (cont->title) + { + pg_wcssize((const unsigned char *) cont->title, strlen(cont->title), + cont->opt->encoding, NULL, &nl_lines, NULL); + lines += nl_lines; + } + + if (!expanded) + { + /* Find the tallest header column */ + for (i = 0; i < cont->ncolumns; i++) + { + if (header_height[i] > max_lines) + max_lines = header_height[i]; + } + + /* Add height of tallest header column */ + lines += max_lines; + max_lines = 0; + } + + /* Count all footer lines */ for (f = cont->footers; f; f = f->next) - lines++; + { + pg_wcssize((const unsigned char *) f->data, strlen(f->data), + cont->opt->encoding, NULL, &nl_lines, NULL); + lines += nl_lines; + } } *fout = PageOutput(lines, cont->opt); -- 2.51.0 --h4pjkjw5wkhovhea Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="test-psql-pager.py" # This script runs different test cases through psql to find the maximum number # of lines that still trigger psql to use the pager. # # Termios is used to set the terminal size. # # Use environment variable PATH to select the psql binary to test. Use libpq # environment variables to select a database for testing in which this script # can run the test setup. import argparse import os import os.path import subprocess import sys import tempfile import time import termios import typing def run_tests(outfile): # Prepare database schema proc = subprocess.run(['psql', '-X'], text=True, input=r''' \set ON_ERROR_STOP on BEGIN; CREATE OR REPLACE FUNCTION generate_lines(n int) RETURNS TABLE (lines text) LANGUAGE sql AS $$ SELECT string_agg(s::text, e'\n') FROM generate_series(1, n) s $$; -- This creates a view name and column name with 24 line breaks when truncated -- to the default NAMEDATALEN (63 = 9*2 + 15*3) SELECT format('CREATE OR REPLACE VIEW %I (c) AS SELECT null;' 'CREATE OR REPLACE VIEW nl_column (%1$I) AS SELECT null;', string_agg(s::text, e'\n')) FROM generate_series(1, current_setting('max_identifier_length')::int) s \gexec COMMIT; ''').check_returncode() testcases = [] for cmd in commands: # Repeat each command with every combination of flags that affect the # number of output lines. for flags in range(1 << 3): testcases.append(Testcase( cmd=cmd, unaligned=flags & 1, tuples_only=flags & (1 << 1), expanded=flags & (1 << 2), )) # Remember current term size save_term_size = termios.tcgetwinsize(sys.stdout.fileno()) max_paged_lines = [] for tc in testcases: max_paged_lines.append(find_max_paged_lines(tc.psql_args())) # Restore term size termios.tcsetwinsize(sys.stdout.fileno(), save_term_size) # Print the testcase results for tc, lines in zip(testcases, max_paged_lines): flags = '' flags += 'A' if tc.unaligned else '-' flags += 't' if tc.tuples_only else '-' flags += 'x' if tc.expanded else '-' # Make sure we get one output line per testcase cmd = tc.cmd.replace('\n', r'\n') print(f"{lines:2} {flags} {cmd}", file=outfile) def run_psql_with_pager(args): with tempfile.NamedTemporaryFile() as tmp: mtime_before = os.stat(tmp.name).st_mtime_ns env = { # Inherit environment variables (especially PATH and libpq-specific # ones). **os.environ, # Set PAGER so that we can tell from the temp file's mtime that the # pager was triggered. 'PAGER': f'touch {tmp.name}', } proc = subprocess.run(['psql', '-X', *args], env=env) if proc.returncode: return None mtime_after = os.stat(tmp.name).st_mtime_ns pager_used = mtime_after > mtime_before return pager_used def find_max_paged_lines(psql_args): # Binary search the maximum number of lines at which psql still triggers # the pager. min_lines = 1 max_lines = 100 cols = 100 # sufficient for our test cases while min_lines <= max_lines: lines = min_lines + (max_lines - min_lines) // 2 termios.tcsetwinsize(sys.stdout.fileno(), (lines, cols)) pager_used = run_psql_with_pager(psql_args) if pager_used is None: return -1 if pager_used: min_lines = lines + 1 else: max_lines = lines - 1 return max_lines class Testcase(typing.NamedTuple): cmd: str unaligned: bool tuples_only: bool expanded: bool def psql_args(self): args = ['-c', self.cmd] if self.unaligned: args.append('-A') if self.tuples_only: args.append('-t') if self.expanded: args.append('-x') return args # The SQL and meta commands we will be testing commands = [ 'SELECT * FROM generate_lines(25)', 'SELECT * FROM nl_column', '\\d nl_column', '\\d+ nl_column', '\\d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"', '\\d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n"', ] parser = argparse.ArgumentParser() parser.add_argument('expectfile', metavar='FILE', help="file with expected test results") args = parser.parse_args() basename, _ = os.path.splitext(args.expectfile) outfile = basename + '.tmp' difffile = basename + '.diff' with open(outfile, 'w') as fp: run_tests(fp) with open(difffile, 'w') as fp: if os.path.isfile(args.expectfile): srcfile = args.expectfile else: srcfile = os.devnull proc = subprocess.run(['diff', '-u', srcfile, outfile], stdout=fp) if proc.returncode: print() print("Test output does not match the expected output.") print(f'The differences can be viewed in file "{difffile}".') exit(1) --h4pjkjw5wkhovhea Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="0-master.out" 27 --- SELECT * FROM generate_lines(25) 2 A-- SELECT * FROM generate_lines(25) 27 -t- SELECT * FROM generate_lines(25) 2 At- SELECT * FROM generate_lines(25) 2 --x SELECT * FROM generate_lines(25) 2 A-x SELECT * FROM generate_lines(25) 2 -tx SELECT * FROM generate_lines(25) 2 Atx SELECT * FROM generate_lines(25) 27 --- SELECT * FROM nl_column 2 A-- SELECT * FROM nl_column 27 -t- SELECT * FROM nl_column 2 At- SELECT * FROM nl_column 2 --x SELECT * FROM nl_column 2 A-x SELECT * FROM nl_column 2 -tx SELECT * FROM nl_column 2 Atx SELECT * FROM nl_column 27 --- \d nl_column 2 A-- \d nl_column 27 -t- \d nl_column 2 At- \d nl_column 27 --x \d nl_column 2 A-x \d nl_column 27 -tx \d nl_column 2 Atx \d nl_column 29 --- \d+ nl_column 4 A-- \d+ nl_column 27 -t- \d+ nl_column 2 At- \d+ nl_column 29 --x \d+ nl_column 4 A-x \d+ nl_column 27 -tx \d+ nl_column 2 Atx \d+ nl_column 3 --- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 A-- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -t- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 At- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 --x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 A-x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -tx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 Atx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 --- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 4 A-- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -t- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 At- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 --x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 4 A-x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -tx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 Atx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" --h4pjkjw5wkhovhea Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="1-refactor.out" 27 --- SELECT * FROM generate_lines(25) 27 A-- SELECT * FROM generate_lines(25) 27 -t- SELECT * FROM generate_lines(25) 27 At- SELECT * FROM generate_lines(25) 27 --x SELECT * FROM generate_lines(25) 27 A-x SELECT * FROM generate_lines(25) 27 -tx SELECT * FROM generate_lines(25) 27 Atx SELECT * FROM generate_lines(25) 27 --- SELECT * FROM nl_column 27 A-- SELECT * FROM nl_column 27 -t- SELECT * FROM nl_column 27 At- SELECT * FROM nl_column 27 --x SELECT * FROM nl_column 27 A-x SELECT * FROM nl_column 27 -tx SELECT * FROM nl_column 27 Atx SELECT * FROM nl_column 27 --- \d nl_column 27 A-- \d nl_column 27 -t- \d nl_column 27 At- \d nl_column 27 --x \d nl_column 27 A-x \d nl_column 27 -tx \d nl_column 27 Atx \d nl_column 29 --- \d+ nl_column 29 A-- \d+ nl_column 27 -t- \d+ nl_column 27 At- \d+ nl_column 29 --x \d+ nl_column 29 A-x \d+ nl_column 27 -tx \d+ nl_column 27 Atx \d+ nl_column 3 --- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 A-- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -t- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 At- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 --x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 A-x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -tx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 Atx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 --- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 A-- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -t- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 At- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 --x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 A-x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -tx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 Atx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" --h4pjkjw5wkhovhea Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="2-expanded.out" 27 --- SELECT * FROM generate_lines(25) 27 A-- SELECT * FROM generate_lines(25) 27 -t- SELECT * FROM generate_lines(25) 27 At- SELECT * FROM generate_lines(25) 27 --x SELECT * FROM generate_lines(25) 27 A-x SELECT * FROM generate_lines(25) 27 -tx SELECT * FROM generate_lines(25) 27 Atx SELECT * FROM generate_lines(25) 27 --- SELECT * FROM nl_column 27 A-- SELECT * FROM nl_column 27 -t- SELECT * FROM nl_column 27 At- SELECT * FROM nl_column 51 --x SELECT * FROM nl_column 51 A-x SELECT * FROM nl_column 51 -tx SELECT * FROM nl_column 51 Atx SELECT * FROM nl_column 27 --- \d nl_column 27 A-- \d nl_column 27 -t- \d nl_column 27 At- \d nl_column 27 --x \d nl_column 27 A-x \d nl_column 27 -tx \d nl_column 27 Atx \d nl_column 29 --- \d+ nl_column 29 A-- \d+ nl_column 27 -t- \d+ nl_column 27 At- \d+ nl_column 29 --x \d+ nl_column 29 A-x \d+ nl_column 27 -tx \d+ nl_column 27 Atx \d+ nl_column 3 --- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 A-- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -t- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 At- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 --x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 A-x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -tx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 Atx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 --- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 A-- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -t- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 At- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 --x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 5 A-x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 -tx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 3 Atx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" --h4pjkjw5wkhovhea Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="3-header-footer.out" 27 --- SELECT * FROM generate_lines(25) 27 A-- SELECT * FROM generate_lines(25) 26 -t- SELECT * FROM generate_lines(25) 26 At- SELECT * FROM generate_lines(25) 26 --x SELECT * FROM generate_lines(25) 26 A-x SELECT * FROM generate_lines(25) 26 -tx SELECT * FROM generate_lines(25) 26 Atx SELECT * FROM generate_lines(25) 27 --- SELECT * FROM nl_column 27 A-- SELECT * FROM nl_column 2 -t- SELECT * FROM nl_column 2 At- SELECT * FROM nl_column 26 --x SELECT * FROM nl_column 26 A-x SELECT * FROM nl_column 26 -tx SELECT * FROM nl_column 26 Atx SELECT * FROM nl_column 28 --- \d nl_column 28 A-- \d nl_column 26 -t- \d nl_column 26 At- \d nl_column 28 --x \d nl_column 28 A-x \d nl_column 26 -tx \d nl_column 26 Atx \d nl_column 54 --- \d+ nl_column 54 A-- \d+ nl_column 26 -t- \d+ nl_column 26 At- \d+ nl_column 54 --x \d+ nl_column 54 A-x \d+ nl_column 26 -tx \d+ nl_column 26 Atx \d+ nl_column 28 --- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 28 A-- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 -t- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 At- \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 28 --x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 28 A-x \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 -tx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 Atx \d "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 30 --- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 30 A-- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 -t- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 At- \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 30 --x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 30 A-x \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 -tx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" 2 Atx \d+ "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n" --h4pjkjw5wkhovhea--