|
8 | 8 | from argparse import ArgumentParser, Namespace
|
9 | 9 | from collections import defaultdict
|
10 | 10 | from concurrent.futures import ThreadPoolExecutor, as_completed
|
11 |
| -from datetime import datetime |
| 11 | +from datetime import datetime, timezone |
12 | 12 | from typing import Dict, List, Optional, Sequence, Tuple, Union
|
13 | 13 | from urllib.parse import urlparse
|
14 | 14 |
|
@@ -470,6 +470,40 @@ def rmdir(args: Namespace):
|
470 | 470 | client.delete(path)
|
471 | 471 |
|
472 | 472 |
|
| 473 | +def touch(args: Namespace): |
| 474 | + for url in args.path: |
| 475 | + client = _client_for_url(url) |
| 476 | + for path in _glob_path(client, _path_for_url(url)): |
| 477 | + if args.access_time and args.modification_time: |
| 478 | + raise ValueError( |
| 479 | + "--access-time and --modification-time cannot both be passed" |
| 480 | + ) |
| 481 | + timestamp = None |
| 482 | + if args.timestamp: |
| 483 | + timestamp = datetime.strptime(args.timestamp, r"%Y%m%d:%H%M%S") |
| 484 | + |
| 485 | + try: |
| 486 | + status = client.get_file_info(path) |
| 487 | + if timestamp is None: |
| 488 | + timestamp = datetime.now(timezone.utc) |
| 489 | + except FileNotFoundError: |
| 490 | + if args.only_change: |
| 491 | + return |
| 492 | + |
| 493 | + client.create(path).close() |
| 494 | + status = client.get_file_info(path) |
| 495 | + |
| 496 | + if timestamp: |
| 497 | + access_time = int(timestamp.timestamp() * 1000) |
| 498 | + modification_time = int(timestamp.timestamp() * 1000) |
| 499 | + if args.access_time: |
| 500 | + modification_time = status.modification_time |
| 501 | + if args.modification_time: |
| 502 | + access_time = status.access_time |
| 503 | + |
| 504 | + client.set_times(path, modification_time, access_time) |
| 505 | + |
| 506 | + |
473 | 507 | def main(in_args: Optional[Sequence[str]] = None):
|
474 | 508 | parser = ArgumentParser(
|
475 | 509 | description="""Command line utility for interacting with HDFS using hdfs-native.
|
@@ -740,6 +774,45 @@ def main(in_args: Optional[Sequence[str]] = None):
|
740 | 774 | )
|
741 | 775 | rmdir_parser.set_defaults(func=rmdir)
|
742 | 776 |
|
| 777 | + touch_parser = subparsers.add_parser( |
| 778 | + "touch", |
| 779 | + help="Updates the access and modification times of a file or creates it if it doesn't exist", |
| 780 | + description="""Updates the access and modification times of the file specified by the <path> to |
| 781 | + the current time. If the file does not exist, then a zero length file is created |
| 782 | + at <path> with current time as the timestamp of that <path>.""", |
| 783 | + ) |
| 784 | + touch_parser.add_argument( |
| 785 | + "-a", |
| 786 | + "--access-time", |
| 787 | + action="store_true", |
| 788 | + default=False, |
| 789 | + help="Only change the access time", |
| 790 | + ) |
| 791 | + touch_parser.add_argument( |
| 792 | + "-m", |
| 793 | + "--modification-time", |
| 794 | + action="store_true", |
| 795 | + default=False, |
| 796 | + help="Only change the modification time", |
| 797 | + ) |
| 798 | + touch_parser.add_argument( |
| 799 | + "-t", |
| 800 | + "--timestamp", |
| 801 | + help="Use specified timestamp instead of current time in the format yyyyMMdd:HHmmss", |
| 802 | + ) |
| 803 | + touch_parser.add_argument( |
| 804 | + "-c", |
| 805 | + "--only-change", |
| 806 | + action="store_true", |
| 807 | + default=False, |
| 808 | + help="Don't create the file if it doesn't exist", |
| 809 | + ) |
| 810 | + touch_parser.add_argument( |
| 811 | + "path", |
| 812 | + nargs="+", |
| 813 | + ) |
| 814 | + touch_parser.set_defaults(func=touch) |
| 815 | + |
743 | 816 | args = parser.parse_args(in_args)
|
744 | 817 | args.func(args)
|
745 | 818 |
|
|
0 commit comments