mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-25 03:14:30 +02:00
--tsv output option, closes #193
This commit is contained in:
parent
771bd81b62
commit
afee15f04b
3 changed files with 65 additions and 28 deletions
20
docs/cli.rst
20
docs/cli.rst
|
|
@ -122,14 +122,14 @@ You can use the ``--json-cols`` option to automatically detect these JSON column
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]-
|
||||||
|
|
||||||
.. _cli_query_csv:
|
.. _cli_query_csv:
|
||||||
|
|
||||||
Running queries and returning CSV
|
Running queries and returning CSV
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
You can use the ``--csv`` option (or ``-c`` shortcut) to return results as CSV::
|
You can use the ``--csv`` option to return results as CSV::
|
||||||
|
|
||||||
$ sqlite-utils dogs.db "select * from dogs" --csv
|
$ sqlite-utils dogs.db "select * from dogs" --csv
|
||||||
id,age,name
|
id,age,name
|
||||||
|
|
@ -142,6 +142,13 @@ This will default to including the column names as a header row. To exclude the
|
||||||
1,4,Cleo
|
1,4,Cleo
|
||||||
2,2,Pancakes
|
2,2,Pancakes
|
||||||
|
|
||||||
|
Use ``--tsv`` instead of ``--csv`` to get back tab-separated values::
|
||||||
|
|
||||||
|
$ sqlite-utils dogs.db "select * from dogs" --tsv
|
||||||
|
id age name
|
||||||
|
1 4 Cleo
|
||||||
|
2 2 Pancakes
|
||||||
|
|
||||||
.. _cli_query_table:
|
.. _cli_query_table:
|
||||||
|
|
||||||
Running queries and outputting a table
|
Running queries and outputting a table
|
||||||
|
|
@ -189,7 +196,7 @@ You can return every row in a specified table using the ``rows`` command::
|
||||||
[{"id": 1, "age": 4, "name": "Cleo"},
|
[{"id": 1, "age": 4, "name": "Cleo"},
|
||||||
{"id": 2, "age": 2, "name": "Pancakes"}]
|
{"id": 2, "age": 2, "name": "Pancakes"}]
|
||||||
|
|
||||||
This command accepts the same output options as ``query`` - so you can pass ``--nl``, ``--csv``, ``--no-headers``, ``--table`` and ``--fmt``.
|
This command accepts the same output options as ``query`` - so you can pass ``--nl``, ``--csv``, ``--tsv``, ``--no-headers``, ``--table`` and ``--fmt``.
|
||||||
|
|
||||||
.. _cli_tables:
|
.. _cli_tables:
|
||||||
|
|
||||||
|
|
@ -203,7 +210,7 @@ You can list the names of tables in a database using the ``tables`` command::
|
||||||
{"table": "cats"},
|
{"table": "cats"},
|
||||||
{"table": "chickens"}]
|
{"table": "chickens"}]
|
||||||
|
|
||||||
You can output this list in CSV using the ``--csv`` option::
|
You can output this list in CSV using the ``--csv`` or ``--tsv`` options::
|
||||||
|
|
||||||
$ sqlite-utils tables mydb.db --csv --no-headers
|
$ sqlite-utils tables mydb.db --csv --no-headers
|
||||||
dogs
|
dogs
|
||||||
|
|
@ -241,7 +248,7 @@ Use ``--schema`` to include the schema of each table::
|
||||||
[age] INTEGER,
|
[age] INTEGER,
|
||||||
[name] TEXT)
|
[name] TEXT)
|
||||||
|
|
||||||
The ``--nl``, ``--csv`` and ``--table`` options are all available.
|
The ``--nl``, ``--csv``, ``--tsv`` and ``--table`` options are all available.
|
||||||
|
|
||||||
.. _cli_views:
|
.. _cli_views:
|
||||||
|
|
||||||
|
|
@ -263,6 +270,7 @@ It takes the same options as the ``tables`` command:
|
||||||
* ``--counts``
|
* ``--counts``
|
||||||
* ``--nl``
|
* ``--nl``
|
||||||
* ``--csv``
|
* ``--csv``
|
||||||
|
* ``--tsv``
|
||||||
* ``--table``
|
* ``--table``
|
||||||
|
|
||||||
.. _cli_inserting_data:
|
.. _cli_inserting_data:
|
||||||
|
|
@ -894,7 +902,7 @@ Once you have configured full-text search for a table, you can search it using `
|
||||||
|
|
||||||
$ sqlite-utils search mydb.db documents searchterm
|
$ sqlite-utils search mydb.db documents searchterm
|
||||||
|
|
||||||
This command accepts the same output options as ``sqlite-utils query``: ``--table``, ``--csv``, ``--nl`` etc.
|
This command accepts the same output options as ``sqlite-utils query``: ``--table``, ``--csv``, ``--tsv``, ``--nl`` etc.
|
||||||
|
|
||||||
By default it shows the most relevant matches first. You can specify a different sort order using the ``-o`` option, which can take a column or a column followed by ``desc``::
|
By default it shows the most relevant matches first. You can specify a different sort order using the ``-o`` option, which can take a column or a column followed by ``desc``::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ def output_options(fn):
|
||||||
default=False,
|
default=False,
|
||||||
),
|
),
|
||||||
click.option("--csv", is_flag=True, help="Output CSV"),
|
click.option("--csv", is_flag=True, help="Output CSV"),
|
||||||
|
click.option("--tsv", is_flag=True, help="Output TSV"),
|
||||||
click.option("--no-headers", is_flag=True, help="Omit CSV headers"),
|
click.option("--no-headers", is_flag=True, help="Omit CSV headers"),
|
||||||
click.option("-t", "--table", is_flag=True, help="Output as a table"),
|
click.option("-t", "--table", is_flag=True, help="Output as a table"),
|
||||||
click.option(
|
click.option(
|
||||||
|
|
@ -120,6 +121,7 @@ def tables(
|
||||||
nl,
|
nl,
|
||||||
arrays,
|
arrays,
|
||||||
csv,
|
csv,
|
||||||
|
tsv,
|
||||||
no_headers,
|
no_headers,
|
||||||
table,
|
table,
|
||||||
fmt,
|
fmt,
|
||||||
|
|
@ -161,8 +163,8 @@ def tables(
|
||||||
|
|
||||||
if table:
|
if table:
|
||||||
print(tabulate.tabulate(_iter(), headers=headers, tablefmt=fmt))
|
print(tabulate.tabulate(_iter(), headers=headers, tablefmt=fmt))
|
||||||
elif csv:
|
elif csv or tsv:
|
||||||
writer = csv_std.writer(sys.stdout)
|
writer = csv_std.writer(sys.stdout, dialect="excel-tab" if tsv else "excel")
|
||||||
if not no_headers:
|
if not no_headers:
|
||||||
writer.writerow(headers)
|
writer.writerow(headers)
|
||||||
for row in _iter():
|
for row in _iter():
|
||||||
|
|
@ -201,6 +203,7 @@ def views(
|
||||||
nl,
|
nl,
|
||||||
arrays,
|
arrays,
|
||||||
csv,
|
csv,
|
||||||
|
tsv,
|
||||||
no_headers,
|
no_headers,
|
||||||
table,
|
table,
|
||||||
fmt,
|
fmt,
|
||||||
|
|
@ -218,6 +221,7 @@ def views(
|
||||||
nl=nl,
|
nl=nl,
|
||||||
arrays=arrays,
|
arrays=arrays,
|
||||||
csv=csv,
|
csv=csv,
|
||||||
|
tsv=tsv,
|
||||||
no_headers=no_headers,
|
no_headers=no_headers,
|
||||||
table=table,
|
table=table,
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
|
|
@ -930,6 +934,7 @@ def query(
|
||||||
nl,
|
nl,
|
||||||
arrays,
|
arrays,
|
||||||
csv,
|
csv,
|
||||||
|
tsv,
|
||||||
no_headers,
|
no_headers,
|
||||||
table,
|
table,
|
||||||
fmt,
|
fmt,
|
||||||
|
|
@ -958,8 +963,8 @@ def query(
|
||||||
sys.stdout.write(str(data))
|
sys.stdout.write(str(data))
|
||||||
elif table:
|
elif table:
|
||||||
print(tabulate.tabulate(list(cursor), headers=headers, tablefmt=fmt))
|
print(tabulate.tabulate(list(cursor), headers=headers, tablefmt=fmt))
|
||||||
elif csv:
|
elif csv or tsv:
|
||||||
writer = csv_std.writer(sys.stdout)
|
writer = csv_std.writer(sys.stdout, dialect="excel-tab" if tsv else "excel")
|
||||||
if not no_headers:
|
if not no_headers:
|
||||||
writer.writerow(headers)
|
writer.writerow(headers)
|
||||||
for row in cursor:
|
for row in cursor:
|
||||||
|
|
@ -1003,6 +1008,7 @@ def search(
|
||||||
nl,
|
nl,
|
||||||
arrays,
|
arrays,
|
||||||
csv,
|
csv,
|
||||||
|
tsv,
|
||||||
no_headers,
|
no_headers,
|
||||||
table,
|
table,
|
||||||
fmt,
|
fmt,
|
||||||
|
|
@ -1039,6 +1045,7 @@ def search(
|
||||||
nl=nl,
|
nl=nl,
|
||||||
arrays=arrays,
|
arrays=arrays,
|
||||||
csv=csv,
|
csv=csv,
|
||||||
|
tsv=tsv,
|
||||||
no_headers=no_headers,
|
no_headers=no_headers,
|
||||||
table=table,
|
table=table,
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
|
|
@ -1065,6 +1072,7 @@ def rows(
|
||||||
nl,
|
nl,
|
||||||
arrays,
|
arrays,
|
||||||
csv,
|
csv,
|
||||||
|
tsv,
|
||||||
no_headers,
|
no_headers,
|
||||||
table,
|
table,
|
||||||
fmt,
|
fmt,
|
||||||
|
|
@ -1079,6 +1087,7 @@ def rows(
|
||||||
nl=nl,
|
nl=nl,
|
||||||
arrays=arrays,
|
arrays=arrays,
|
||||||
csv=csv,
|
csv=csv,
|
||||||
|
tsv=tsv,
|
||||||
no_headers=no_headers,
|
no_headers=no_headers,
|
||||||
table=table,
|
table=table,
|
||||||
fmt=fmt,
|
fmt=fmt,
|
||||||
|
|
|
||||||
|
|
@ -62,24 +62,37 @@ def test_tables_counts_and_columns(db_path):
|
||||||
) == result.output.strip()
|
) == result.output.strip()
|
||||||
|
|
||||||
|
|
||||||
def test_tables_counts_and_columns_csv(db_path):
|
@pytest.mark.parametrize(
|
||||||
|
"format,expected",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"--csv",
|
||||||
|
(
|
||||||
|
"table,count,columns\n"
|
||||||
|
'Gosh,0,"c1\n'
|
||||||
|
"c2\n"
|
||||||
|
'c3"\n'
|
||||||
|
'Gosh2,0,"c1\n'
|
||||||
|
"c2\n"
|
||||||
|
'c3"\n'
|
||||||
|
'lots,30,"id\n'
|
||||||
|
'age"'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"--tsv",
|
||||||
|
"table\tcount\tcolumns\nGosh\t0\t['c1', 'c2', 'c3']\nGosh2\t0\t['c1', 'c2', 'c3']\nlots\t30\t['id', 'age']",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_tables_counts_and_columns_csv(db_path, format, expected):
|
||||||
db = Database(db_path)
|
db = Database(db_path)
|
||||||
with db.conn:
|
with db.conn:
|
||||||
db["lots"].insert_all([{"id": i, "age": i + 1} for i in range(30)])
|
db["lots"].insert_all([{"id": i, "age": i + 1} for i in range(30)])
|
||||||
result = CliRunner().invoke(
|
result = CliRunner().invoke(
|
||||||
cli.cli, ["tables", "--counts", "--columns", "--csv", db_path]
|
cli.cli, ["tables", "--counts", "--columns", format, db_path]
|
||||||
)
|
)
|
||||||
assert (
|
assert result.output.strip() == expected
|
||||||
"table,count,columns\n"
|
|
||||||
'Gosh,0,"c1\n'
|
|
||||||
"c2\n"
|
|
||||||
'c3"\n'
|
|
||||||
'Gosh2,0,"c1\n'
|
|
||||||
"c2\n"
|
|
||||||
'c3"\n'
|
|
||||||
'lots,30,"id\n'
|
|
||||||
'age"'
|
|
||||||
) == result.output.strip()
|
|
||||||
|
|
||||||
|
|
||||||
def test_tables_schema(db_path):
|
def test_tables_schema(db_path):
|
||||||
|
|
@ -809,7 +822,14 @@ def test_insert_alter(db_path, tmpdir):
|
||||||
] == db.execute_returning_dicts("select foo, n, baz from from_json_nl")
|
] == db.execute_returning_dicts("select foo, n, baz from from_json_nl")
|
||||||
|
|
||||||
|
|
||||||
def test_query_csv(db_path):
|
@pytest.mark.parametrize(
|
||||||
|
"format,expected",
|
||||||
|
[
|
||||||
|
("--csv", "id,name,age\n1,Cleo,4\n2,Pancakes,2\n"),
|
||||||
|
("--tsv", "id\tname\tage\n1\tCleo\t4\n2\tPancakes\t2\n"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_query_csv(db_path, format, expected):
|
||||||
db = Database(db_path)
|
db = Database(db_path)
|
||||||
with db.conn:
|
with db.conn:
|
||||||
db["dogs"].insert_all(
|
db["dogs"].insert_all(
|
||||||
|
|
@ -819,15 +839,15 @@ def test_query_csv(db_path):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
result = CliRunner().invoke(
|
result = CliRunner().invoke(
|
||||||
cli.cli, [db_path, "select id, name, age from dogs", "--csv"]
|
cli.cli, [db_path, "select id, name, age from dogs", format]
|
||||||
)
|
)
|
||||||
assert 0 == result.exit_code
|
assert 0 == result.exit_code
|
||||||
assert "id,name,age\n1,Cleo,4\n2,Pancakes,2\n" == result.output
|
assert result.output == expected
|
||||||
# Test the no-headers option:
|
# Test the no-headers option:
|
||||||
result = CliRunner().invoke(
|
result = CliRunner().invoke(
|
||||||
cli.cli, [db_path, "select id, name, age from dogs", "--no-headers", "--csv"]
|
cli.cli, [db_path, "select id, name, age from dogs", "--no-headers", format]
|
||||||
)
|
)
|
||||||
assert "1,Cleo,4\n2,Pancakes,2\n" == result.output
|
assert result.output.strip() == "\n".join(expected.split("\n")[1:]).strip()
|
||||||
|
|
||||||
|
|
||||||
_all_query = "select id, name, age from dogs"
|
_all_query = "select id, name, age from dogs"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue