├── .env ├── .github └── workflows │ └── test-scripts.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── bashx ├── bashx.src ├── README.md ├── actions │ └── action3.xsh └── tests └── src ├── README.md ├── actions ├── README.md ├── _bashx.xsh ├── _bashx │ ├── README.md │ ├── commonAction.xsh │ ├── commonAction │ │ ├── add.xsh │ │ └── remove.xsh │ ├── task │ │ ├── action.xsh │ │ ├── event.xsh │ │ ├── init │ │ │ ├── config.xsh │ │ │ └── project.xsh │ │ ├── resource.xsh │ │ ├── test.xsh │ │ └── util.xsh │ └── taskResource │ │ └── add.xsh ├── _dev-doc.xsh ├── _install-as-command.xsh ├── _run-tests-all.xsh ├── _run-tests-docker.xsh ├── _run-tests.xsh └── help.xsh ├── bootstrap.src ├── config.init.src ├── config.src ├── constants.src ├── init.sh ├── setup.sh ├── tests ├── 000.asserts.xsh ├── README.md ├── app.info.xsh ├── args.xsh ├── array.contains.xsh ├── checkError.xsh ├── file.contains.xsh ├── file.name.xsh ├── function.exists.xsh ├── function.void.xsh ├── isNumber.xsh ├── isRoot.xsh ├── log.alert.xsh ├── log.cmd.xsh ├── log.warn.xsh ├── log.xsh ├── random.xsh ├── removeFormat.xsh ├── screen.width.xsh ├── str.in.xsh ├── str.len.xsh ├── str.ltrim.xsh ├── str.pos.xsh ├── str.repeat.xsh ├── str.replace.xsh ├── str.rtrim.xsh ├── str.sub.xsh ├── str.toLower.xsh ├── str.toUpper.xsh ├── str.trim.xsh ├── temp.path.xsh └── user.timeout.xsh └── utils ├── README.md ├── app ├── backtrace.xsh ├── error.xsh ├── exit.xsh ├── info.xsh └── run.xsh ├── args.xsh ├── array └── contains.xsh ├── asserts └── load.xsh ├── checkError.xsh ├── checkErrorEnd.xsh ├── code ├── scriptMinify.xsh └── variableClean.xsh ├── config └── test.xsh ├── file ├── contains.xsh ├── ensureLineExists.xsh └── name.xsh ├── function ├── exists.xsh ├── load.xsh └── void.xsh ├── isBoolean.xsh ├── isNumber.xsh ├── isRoot.xsh ├── log.xsh ├── log ├── alert.xsh ├── cmd.xsh ├── line.xsh ├── rewrite.xsh ├── time.xsh ├── title.xsh ├── waiting.xsh └── warn.xsh ├── mktemp.xsh ├── now ├── date.xsh ├── dateTime.xsh └── time.xsh ├── on ├── exit.xsh ├── stderr.xsh ├── stdinfo.xsh └── stdout.xsh ├── random.xsh ├── required.xsh ├── rootValidate.xsh ├── screen └── width.xsh ├── sed.xsh ├── str ├── escape.xsh ├── in.xsh ├── len.xsh ├── ltrim.xsh ├── pos.xsh ├── repeat.xsh ├── replace.xsh ├── rtrim.xsh ├── sub.xsh ├── toLower.xsh ├── toUpper.xsh └── trim.xsh ├── style.xsh ├── style └── clean.xsh ├── sudo.xsh ├── throw.xsh ├── throw ├── invalidParam.xsh └── notImplemented.xsh ├── usage.xsh ├── usage └── file.xsh ├── user ├── choice.xsh ├── confirm.xsh ├── input.xsh ├── pause.xsh └── timeout.xsh └── wait ├── until.xsh └── untilExists.xsh /.env: -------------------------------------------------------------------------------- 1 | BASHX_APP_TITLE='BashX' 2 | BASHX_APP_VERSION=Master 3 | -------------------------------------------------------------------------------- /.github/workflows/test-scripts.yml: -------------------------------------------------------------------------------- 1 | name: Test Scripts 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test-local: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Local test 17 | run: ./bashx _run-tests 18 | test-docker-ubuntu: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Local test 23 | run: ./bashx _run-tests-docker ubuntu 24 | test-docker-debian8: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v2 28 | - name: Local test 29 | run: ./bashx _run-tests-docker debian:8 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | *.log 3 | **/.history 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "foxundermoon.shell-format", 4 | "truman.autocomplate-shell" 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/.history": true, 4 | "bashx.src/tests": true 5 | }, 6 | "files.watcherExclude": { 7 | "**/.history": true, 8 | "bashx.src/tests": true 9 | }, 10 | "files.exclude": { 11 | "**/.history": true, 12 | "bashx.src/tests": true 13 | }, 14 | "cSpell.words": [ 15 | "BASHX", 16 | "CTYPE", 17 | "Cuomo", 18 | "elif", 19 | "expandtab", 20 | "shiftwidth", 21 | "smarttab", 22 | "softtabstop" 23 | ], 24 | "files.associations": { 25 | "*.xsh": "shellscript", 26 | "*.src": "shellscript" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | BashX: Bash Extended Framework 635 | Copyright (C) 2022 Eduardo Cuomo 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see 649 | https://github.com/reduardo7/bashx/blob/master/LICENSE. 650 | 651 | Also add information on how to contact you by electronic and paper mail. 652 | 653 | If the program does terminal interaction, make it output a short 654 | notice like this when it starts in an interactive mode: 655 | 656 | BashX Copyright (C) 2022 Eduardo Cuomo 657 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 658 | This is free software, and you are welcome to redistribute it 659 | under certain conditions; type `show c' for details. 660 | 661 | The hypothetical commands `show w' and `show c' should show the appropriate 662 | parts of the General Public License. Of course, your program's commands 663 | might be different; for a GUI interface, you would use an "about box". 664 | 665 | You should also get your employer (if you work as a programmer) or school, 666 | if any, to sign a "copyright disclaimer" for the program, if necessary. 667 | For more information on this, and how to apply and follow the GNU GPL, see 668 | https://github.com/reduardo7/bashx/blob/master/LICENSE. 669 | 670 | The GNU General Public License does not permit incorporating your program 671 | into proprietary programs. If your program is a subroutine library, you 672 | may consider it more useful to permit linking proprietary applications with 673 | the library. If this is what you want to do, use the GNU Lesser General 674 | Public License instead of this License. But first, please read 675 | . 676 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BashX | Bash eXtended 2 | 3 | ## Description 4 | 5 | - **_Bash Framework_**. 6 | - Helps to bash scripting. 7 | - Add common functions to help the developer. 8 | - Compatible with _Android Shell_. 9 | - Auto-documentation/help generation. 10 | 11 | ### Why use it 12 | 13 | - Very easy to implement. 14 | - No additional components required. 15 | - No need to learn a new language. 16 | - No need to learn a new syntax. 17 | - Common functions and utils already implemented. 18 | - Auto-documentation/help from source code comments. 19 | - Solution to common _SO_ compatibility problems (`sed -i ...` on _Linux_ and `sed -i '' ...` 20 | on [_Mac_](https://stackoverflow.com/a/16746032/717267)). See [`@sed`](src/utils/sed.xsh) 21 | 22 | ## Quick start 23 | 24 | You can start your project with: 25 | 26 | ```bash 27 | ./bashx _bashx init project {BASHX_VERSION} {PROJECT_PATH} 28 | ``` 29 | 30 | Where: 31 | 32 | - `BASHX_VERSION` is a [_tag_ from this repository](https://github.com/reduardo7/bashx/tags) 33 | - `PROJECT_PATH` is the _script name_ with _full path_. 34 | 35 | ### Examples 36 | 37 | ```bash 38 | ./bashx _bashx init project v3.1.2 my-app 39 | ``` 40 | 41 | ```bash 42 | ./bashx _bashx init project v3.1.2 ~/projects/my-script.sh 43 | ``` 44 | 45 | ## Manual start 46 | 47 | **1)** Add next at beginning of the script file: 48 | 49 | ```bash 50 | #!/usr/bin/env bash 51 | 52 | ############################################################################### 53 | # BashX | https://github.com/reduardo7/bashx 54 | set +ex;export BASHX_VERSION="v3.1.2" 55 | (export LC_CTYPE=C;export LC_ALL=C;export LANG=C;set -e;x() { s="$*";echo "# Error: ${s:-Installation fail}" >&2;exit 1;};d=/dev/null;[ ! -z "$BASHX_VERSION" ] || x BASHX_VERSION is required;export BASHX_DIR="${BASHX_DIR:-${HOME:-/tmp}/.bashx/$BASHX_VERSION}";if [ ! -d "$BASHX_DIR" ];then u="https://raw.githubusercontent.com/reduardo7/bashx/$BASHX_VERSION/src/setup.sh";if type wget >$d 2>&1;then sh -c "$(wget -q $u -O -)" || x;elif type curl >$d 2>&1;then sh -c "$(curl -fsSL $u)" || x;else x wget or curl are required. Install wget or curl to continue;fi;fi) || exit $? 56 | . "${HOME:-/tmp}/.bashx/${BASHX_VERSION}/src/init.sh" 57 | ############################################################################### 58 | ``` 59 | 60 | > See [/bashx](/bashx#L3) 61 | 62 | **2)** Write your code. 63 | 64 | **3)** Optionally, add next at end of the script file, to work as _cli_: 65 | 66 | ```bash 67 | @app.run 68 | ``` 69 | 70 | ### Project Example 71 | 72 | ```bash 73 | #!/usr/bin/env bash 74 | 75 | ############################################################################### 76 | # BashX | https://github.com/reduardo7/bashx 77 | set +ex;export BASHX_VERSION="v3.1.2" 78 | (export LC_CTYPE=C;export LC_ALL=C;export LANG=C;set -e;x() { s="$*";echo "# Error: ${s:-Installation fail}" >&2;exit 1;};d=/dev/null;[ ! -z "$BASHX_VERSION" ] || x BASHX_VERSION is required;export BASHX_DIR="${BASHX_DIR:-${HOME:-/tmp}/.bashx/$BASHX_VERSION}";if [ ! -d "$BASHX_DIR" ];then u="https://raw.githubusercontent.com/reduardo7/bashx/$BASHX_VERSION/src/setup.sh";if type wget >$d 2>&1;then sh -c "$(wget -q $u -O -)" || x;elif type curl >$d 2>&1;then sh -c "$(curl -fsSL $u)" || x;else x wget or curl are required. Install wget or curl to continue;fi;fi) || exit $? 79 | . "${HOME:-/tmp}/.bashx/${BASHX_VERSION}/src/init.sh" 80 | ############################################################################### 81 | 82 | @Actions.action1() { # \\n Action without arguments 83 | @log " 84 | Action 1 85 | Multi-Line 86 | " 87 | } 88 | 89 | @Actions.action2() { # param1 [param2] \\n Action with arguments\\n\\tdescription second line\\nother line 90 | local param1="$1" 91 | local param2="$2" 92 | [ "$param1" != 'asd' ] && @throw.invalidParam param1 93 | 94 | @log Action 2 95 | @log Param1: $1 96 | @log Param2: $2 97 | } 98 | 99 | @app.run 100 | ``` 101 | 102 | #### Project Structure 103 | 104 | ```text 105 | --> project-directory-name # Optional. Container directory. 106 | | # 107 | +-> my-script-name # Required. Main script. 108 | | # 109 | +-> .my-script-name.env # Optional. Config file. 110 | | # 111 | +-> my-script-name.src/ # Optional. Sources. 112 | | # 113 | +-> actions/ # Optional. Actions scripts. 114 | | | # 115 | | +-> [group...] # Script group. Can be multi-level. 116 | | | | # 117 | | | +-> [name].xsh # Test script into group example... (Use with @group.name) 118 | | | # 119 | | +-> [action-name].xsh # Test script example... 120 | | | # 121 | | +-> * # Test script example... 122 | | # 123 | +-> tests/ # Optional. Test scripts. 124 | | | # 125 | | +-> [test-name].xsh # Test script example... 126 | | | # 127 | | +-> * # Test script example... 128 | | # 129 | +-> utils/ # Optional. Utils scripts. 130 | | | # 131 | | +-> [util-name].xsh # Test script example... 132 | | | # 133 | | +-> * # Test script example... 134 | | # 135 | +-> events/ # Optional. Events scripts. Executed in next order: 136 | | | # 137 | | +-> invalid-action.xsh # Optional. Triggered on invalid action called. 138 | | | # 139 | | +-> ready.xsh # Optional. Triggered on ready. 140 | | | # 141 | | +-> start.xsh # Optional. Triggered on start the selected action. 142 | | | # 143 | | +-> error.xsh # Optional. Triggered on error (exit code != 0). 144 | | | # 145 | | +-> finish.xsh # Optional. Triggered on execution finished. 146 | | # 147 | +-> resources/ # Optional. Resources files. 148 | | # 149 | +-> [resource].[ext] # Resource file... 150 | | # 151 | +-> * # Resource file... 152 | ``` 153 | 154 | Valid events options constant: `BX_EVENTS_OPTS`. 155 | 156 | ## Doc 157 | 158 | Go to [`src/README.md`](src/README.md) documentation for more details. 159 | 160 | ### Development Documentation 161 | 162 | Show [_Development Documentation_](src/actions/_dev-doc.xsh) using: 163 | 164 | ```bash 165 | ./bashx _dev-doc 166 | ``` 167 | 168 | ### Framework Utilities 169 | 170 | Show [_Framework Utilities Documentation_](src/actions/_bashx/README.md) using: 171 | 172 | ```bash 173 | ./bashx _bashx 174 | ``` 175 | 176 | ## Global Variables 177 | 178 | ### [`BASHX_COLORS_DISABLED`](src/utils/style.xsh#L92) 179 | 180 | Set vale to `1` to disable **BashX** output colors, disabling the `@style` function. 181 | 182 | **Example:** 183 | 184 | ```bash 185 | BASHX_COLORS_DISABLED=1 ./bashx 186 | ``` 187 | 188 | ## Tips 189 | 190 | ### Print/Log (echo ...) 191 | 192 | - `echo` is used for function output. 193 | - Use `@log` to print log messages. 194 | - Use `@log.warn` to print warning messages. 195 | 196 | ### Command log (set -x) 197 | 198 | - Avoid usage of **BashX** _Functions_ inside of `set -x` section. 199 | 200 | #### Command log examples 201 | 202 | ```bash 203 | ... 204 | ( set -x 205 | ... 206 | echo my test 207 | ... 208 | ) 209 | ... 210 | ``` 211 | 212 | ```bash 213 | ... 214 | set -x 215 | ... 216 | echo my test 217 | ... 218 | set +x 219 | ... 220 | ``` 221 | 222 | ### APP Exit & Error 223 | 224 | - Use `@app.exit` to exit. 225 | - Use `@app.error` to print an error and exit. 226 | 227 | ### Events Workflow 228 | 229 | 1. `src/events/invalid-action.xsh` is triggered if an invalid action was used. 230 | 2. `src/events/ready.xsh` is triggered on the initialization is complete. 231 | 3. `src/events/start.xsh` is triggered before a valid action is called. 232 | 4. `src/events/error.xsh` is triggered when an error has occurred. 233 | 5. `src/events/finish.xsh` is triggered on execution finished. 234 | 235 | Valid events options constant: `BX_EVENTS_OPTS`. 236 | 237 | ## Optimizations 238 | 239 | See: [http://tldp.org/LDP/abs/html/optimizations.html](http://tldp.org/LDP/abs/html/optimizations.html) 240 | 241 | ## Testing with Docker 242 | 243 | ```bash 244 | docker run --rm \ 245 | -v $(pwd):/root/.bashx/master:ro \ 246 | -v $(pwd):/app:ro \ 247 | -w '/app' \ 248 | -ti ubuntu '/app/bashx' 249 | ``` 250 | 251 | ```bash 252 | docker run --rm \ 253 | -v $(pwd):/root/.bashx/master:ro \ 254 | -v $(pwd):/app:ro \ 255 | -w '/app' \ 256 | -ti debian:8 '/app/bashx' 257 | ``` 258 | 259 | ## Notes 260 | 261 | ## VIM 262 | 263 | In order to make [_VIM_](https://www.vim.org) syntax check and code format work, 264 | add the following line at the end of your `.xsh` file: 265 | 266 | ```plain 267 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 268 | ``` 269 | 270 | ### Constants & Config Variables 271 | 272 | All _BashX_ **constants** starts with `BX_`, and **configuration** starts with `BASHX_`. 273 | -------------------------------------------------------------------------------- /bashx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################### 4 | # BashX | https://github.com/reduardo7/bashx 5 | set +ex;export BASHX_VERSION="master" 6 | (export LC_CTYPE=C;export LC_ALL=C;export LANG=C;set -e;x() { s="$*";echo "# Error: ${s:-Installation fail}" >&2;exit 1;};d=/dev/null;[ ! -z "$BASHX_VERSION" ] || x BASHX_VERSION is required;export BASHX_DIR="${BASHX_DIR:-${HOME:-/tmp}/.bashx/$BASHX_VERSION}";if [ ! -d "$BASHX_DIR" ];then u="https://raw.githubusercontent.com/reduardo7/bashx/$BASHX_VERSION/src/setup.sh";if type wget >$d 2>&1 ;then sh -c "$(wget -q $u -O -)" || x;elif type curl >$d 2>&1 ;then sh -c "$(curl -fsSL $u)" || x;else x wget or curl are required. Install wget or curl to continue;fi;fi) || exit $? 7 | . "${HOME:-/tmp}/.bashx/${BASHX_VERSION}/src/init.sh" 8 | ############################################################################### 9 | 10 | ### Begin Example ### 11 | 12 | # _onStdOut() { 13 | # local line="$1" 14 | # echo "++ ${line}" 15 | # } 16 | 17 | # @on.stdout '_onStdOut "${OUT_LINE}"' 18 | 19 | @Actions.action1() { # \\n Action without arguments 20 | set -x 21 | pwd 22 | @log " 23 | Action 1 24 | Multi-Line 25 | " 26 | ls -la 27 | bash 28 | } 29 | 30 | @Actions.action2() { # param1 [param2] \\n Action with arguments\\n\\tdescription second line\\nother line 31 | eval "$(@args 'new:-n|-N' 'path:-p|--path:true')" 32 | set -x 33 | 34 | @log "'n' or 'N' parameter: ${args_new}" 35 | @log "'p' or 'path' parameter: ${args_path[@]} (${#args_path[@]})" 36 | 37 | local param1="$1" 38 | local param2="$2" 39 | [[ "$param1" != 'asd' ]] && @throw.invalidParam param1 40 | 41 | @log Action 2 42 | @log Param1: $1 43 | @log Param2: $2 44 | } 45 | 46 | ### End Example ### 47 | 48 | @app.run 49 | 50 | # BashX example file 51 | 52 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 53 | -------------------------------------------------------------------------------- /bashx.src/README.md: -------------------------------------------------------------------------------- 1 | # BashX example directory 2 | 3 | ## [/actions](actions) 4 | 5 | _BashX_ example **actions**. 6 | -------------------------------------------------------------------------------- /bashx.src/actions/action3.xsh: -------------------------------------------------------------------------------- 1 | ## param1 2 | ## Action 3 description 3 | ## 4 | ## Params: 5 | ## param1: Action3 > param1 6 | 7 | @log hi $1 8 | @log "Params: ${@}" 9 | 10 | # BashX example file 11 | 12 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 13 | -------------------------------------------------------------------------------- /bashx.src/tests: -------------------------------------------------------------------------------- 1 | ../src/tests -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # BashX Sources 2 | 3 | ## [`/bootstrap.src`](bootstrap.src) 4 | 5 | Uncompressed script to initialize the _main APP_. 6 | 7 | It's present at the beginning of _BashX_ entry-point script. 8 | 9 | ## [`/config.init.src`](config.init.src) 10 | 11 | Initial _BashX_ project configuration. 12 | Should be override before load the _bootstrap_ script, 13 | or before call the entrypoint script. 14 | 15 | ## [`/config.src`](config.src) 16 | 17 | _BashX_ project configuration. 18 | Can be override by custom project configuration. 19 | 20 | ## [`/constants.src`](constants.src) 21 | 22 | Readonly _BashX_ internal constants. 23 | 24 | ## [`/init.sh`](init.sh) 25 | 26 | Initial _BashX_ script. 27 | In charge of loading everything before start. 28 | 29 | ## [`/setup.sh`](setup.sh) 30 | 31 | First time install _BashX_ script. 32 | 33 | ## [`/actions`](actions) 34 | 35 | Available _BashX_ **actions** script. 36 | 37 | ## [`/tests`](tests) 38 | 39 | Available _BashX_ **tests** scripts. 40 | 41 | ## [`/utils`](utils) 42 | 43 | Available _BashX_ **utils** scripts. 44 | You can call each one using the prefix `@` and the filename. 45 | 46 | _For example:_ 47 | 48 | ```bash 49 | # src/utils/myScript.xsh 50 | @myScript 51 | 52 | # src/utils/myScript/foo.xsh 53 | @myScript.foo 54 | 55 | # src/utils/helpers/bar.xsh 56 | @helpers.bar 57 | ``` 58 | -------------------------------------------------------------------------------- /src/actions/README.md: -------------------------------------------------------------------------------- 1 | # BashX Framework Actions 2 | 3 | Available _BashX_ **actions**. 4 | 5 | > **Notes** 6 | > 7 | > - All scripts starting with `_` character, will be hidden when 8 | > `help` action is called. 9 | -------------------------------------------------------------------------------- /src/actions/_bashx.xsh: -------------------------------------------------------------------------------- 1 | ## task action value1? value2? value3? 2 | ## BashX Framework Utils. 3 | ## 4 | ## Usage: 5 | ## 6 | ## {task} == `action`, `util`, `test`, `event` 7 | ## {action}: 8 | ## `add`: Add task. 9 | ## `remove`: Remove task. 10 | ## {value1}: Script name. 11 | ## 12 | ## {task} == `event` 13 | ## {action}: 14 | ## `add`: Add event. 15 | ## `remove`: Remove event. 16 | ## {value1}: 17 | ## `invalid-action`: Triggered on invalid action called. 18 | ## `ready`: Triggered on the initialization is complete. 19 | ## `start`: Triggered on start, before a valid action is called. 20 | ## `error`: Triggered on error (exit code != 0). 21 | ## `finish`: Triggered on execution finished. 22 | ## 23 | ## {task} == `init` 24 | ## {action}: 25 | ## `config`: Initialize configuration file. 26 | ## Create configuration file if not exists from 27 | ## ${BASHX_APP_CONFIG_FILE} constant. 28 | ## `project`: Initialize project. 29 | ## {value1}: BashX version for new project. 30 | ## See https://github.com/reduardo7/bashx/releases for available versions. 31 | ## {value2}: Project script name with path. 32 | ## {value3}: New project title. 33 | ## Optional. Default: read from {value2}. 34 | ## 35 | ## {task} == `resource` 36 | ## {action}: 37 | ## `add`: Add new resource. 38 | ## {value1}: File or directory path to add. 39 | ## {value2}: Destination file or directory name. 40 | ## Optional. Default: {value1} base name. 41 | ## 42 | ## Examples: 43 | ## * Add "foo" to Actions: 44 | ## _bashx action add foo 45 | ## * Remove "bar" from Utils: 46 | ## _bashx util remove bar 47 | ## * Initialize project: 48 | ## _bashx init project v3.1.2 ~/project/my-script 'My Super-Script' 49 | 50 | @log.title "$(@style bold color:red)BashX$(@style) Framework Utils" 51 | 52 | local task="$1" 53 | local action="$2" 54 | local value1="$3" 55 | local value2="$4" 56 | local value3="$5" 57 | 58 | if [[ ! -z "${task}" ]]; then 59 | case "${task}" in 60 | init) 61 | ${this}.task.${task}.${action} "${value1}" "${value2}" "${value3}" ;; 62 | *) 63 | ${this}.task.${task} "${action}" "${value1}" "${value2}" "${value3}" ;; 64 | esac 65 | fi 66 | 67 | # Usage 68 | @log 69 | @usage "${this_path}" 70 | @app.exit 1 71 | 72 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 73 | -------------------------------------------------------------------------------- /src/actions/_bashx/README.md: -------------------------------------------------------------------------------- 1 | # BashX Framework Utilities Actions 2 | -------------------------------------------------------------------------------- /src/actions/_bashx/commonAction.xsh: -------------------------------------------------------------------------------- 1 | local action="$1" 2 | local name="$2" 3 | local path="$3" 4 | local script="${path}/${name}.${BX_SCRIPT_EXTENSION}" 5 | 6 | if [[ ! -z "${name}" ]] && [[ ! -z "${action}" ]]; then 7 | case "${action}" in 8 | add) fwUtils.commonAction.add "${name}" "${path}" ;; 9 | remove) fwUtils.commonAction.remove "${name}" "${path}" ;; 10 | *) @log.warn 'Invalid action!' ;; 11 | esac 12 | fi 13 | 14 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 15 | -------------------------------------------------------------------------------- /src/actions/_bashx/commonAction/add.xsh: -------------------------------------------------------------------------------- 1 | local name="$1" 2 | local path="$2" 3 | local script="${path}/${name}.${BX_SCRIPT_EXTENSION}" 4 | 5 | if [[ ! -d "${path}" ]]; then 6 | mkdir -p "${path}" || @app.error "Can not create directory '$(@style bold color:blue)${path}$(@style)'" 7 | @log "Directory '$(@style bold color:blue)${path}$(@style)' $(@style bold color:green)created$(@style)!" 8 | fi 9 | 10 | if [[ -f "${script}" ]]; then 11 | @log.warn "Script '$(@style bold color:blue)${script}$(@style)' already exists!" 12 | else 13 | cat /dev/stdin >"${script}" || @app.error "Can not create file '$(@style bold color:blue)${script}$(@style)'" 14 | @log "Script '$(@style bold color:blue)${script}$(@style)' $(@style bold color:green)added$(@style)!" 15 | fi 16 | 17 | @app.exit 18 | 19 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 20 | -------------------------------------------------------------------------------- /src/actions/_bashx/commonAction/remove.xsh: -------------------------------------------------------------------------------- 1 | if [[ -d "${path}" ]]; then 2 | if [[ -f "${script}" ]]; then 3 | rm -f "${script}" || @app.error "Can not remove file '$(@style bold color:blue)${script}$(@style)'" 4 | @log "File '$(@style bold color:blue)${script}$(@style)' $(@style bold color:red)deleted$(@style)!" 5 | fi 6 | 7 | # Empty directory? 8 | if [[ -z "$(ls -A "${path}")" ]]; then 9 | rm -rf "${path}" || @app.error "Can not remove directory '$(@style bold color:blue)${path}$(@style)'" 10 | @log "Directory '$(@style bold color:blue)${path}$(@style)' now is empty, $(@style bold color:red)deleted$(@style)!" 11 | fi 12 | fi 13 | 14 | @app.exit 15 | 16 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 17 | -------------------------------------------------------------------------------- /src/actions/_bashx/task/action.xsh: -------------------------------------------------------------------------------- 1 | fwUtils.commonAction "$1" "$2" "${BASHX_ACTIONS_PATH}" < "${project_path}" <&1 2>&1 | less 17 | 18 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 19 | -------------------------------------------------------------------------------- /src/actions/_install-as-command.xsh: -------------------------------------------------------------------------------- 1 | ## [-f] action 2 | ## Install auto-complete and bin files in current user shell. 3 | ## 4 | ## Params: 5 | ## action: {Constant} Action to do. Valid values: 6 | ## install: Install auto-complete funcion. Use "-f" to force re-install. 7 | ## uninstall: Uninstall auto-complete function. Use "-f" for no inreractive mode. 8 | ## 9 | ## Options: 10 | ## -f|--force: Force action (no interactive). 11 | 12 | eval "$(@args 'force:-f|--force')" 13 | 14 | [ -z "${HOME}" ] || [ ! -d "${HOME}" ] && @throw.invalidParam HOME 15 | 16 | local action="$1" 17 | 18 | local scrpt="${BX_SCRIPT_FILE_NAME}" 19 | local bcfile="${HOME}/.${scrpt}_completion" 20 | local bcline=". ${bcfile}" 21 | local rcs=(.bashrc .zshrc .shrc) 22 | local l="PATH=\"${BX_SCRIPT_DIR}:\$PATH\" ; export PATH=\"\$PATH\" ; ${bcline}" 23 | local p 24 | local r 25 | 26 | case "$action" in 27 | install) 28 | if ${args_force} || [ ! -f "${bcfile}" ]; then 29 | # Create file 30 | @log "Creating '$(@style bold)${bcfile}$(@style)' file..." 31 | 32 | cat > "${bcfile}" </dev/null 2>/dev/null; then 60 | # bash 61 | complete -F _${scrpt} ${scrpt} 62 | else if type compdef >/dev/null 2>/dev/null; then 63 | # zsh 64 | compdef _${scrpt}_zsh ${scrpt} 65 | fi; fi 66 | EOF 67 | fi 68 | 69 | for r in ${rcs[@]} ; do 70 | p="${HOME}/${r}" 71 | 72 | if [ -f "${p}" ]; then 73 | if grep -q "${l}" "${p}" ; then 74 | @log "Already installed at '$(@style bold)${r}$(@style)'" 75 | else 76 | @log "Installing in '$(@style bold)${r}$(@style)'..." 77 | echo >> "${p}" 78 | echo "${l}" >> "${p}" 79 | fi 80 | fi 81 | done 82 | 83 | @log "Done!" 84 | ;; 85 | uninstall) 86 | @log "Uninstalling..." 87 | 88 | if [ -f "${bcfile}" ]; then 89 | # Delete file 90 | @log "Deleting '$(@style bold)${bcfile}$(@style)' file..." 91 | 92 | if ${args_force}; then 93 | # Force 94 | rm -f "${bcfile}" 95 | else 96 | # Interactive 97 | rm -i "${bcfile}" 98 | fi 99 | fi 100 | 101 | for r in ${rcs[@]} ; do 102 | p="${HOME}/${r}" 103 | if [ -f "${p}" ]; then 104 | if grep "${l}" "${p}" >/dev/null 2>&1 105 | then 106 | @log "Removing from '$(@style bold)${r}$(@style)'..." 107 | if ${args_force}; then 108 | # Force 109 | cat "${p}" | grep -v "${l}" > "${p}.2" && mv -f "${p}.2" "${p}" 110 | else 111 | # Interactive 112 | cat "${p}" | grep -v "${l}" > "${p}.2" && mv -i "${p}.2" "${p}" 113 | fi 114 | fi 115 | fi 116 | done 117 | 118 | @log "Done!" 119 | ;; 120 | *) 121 | # Invalid action 122 | @throw.invalidParam action 123 | ;; 124 | esac 125 | 126 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 127 | -------------------------------------------------------------------------------- /src/actions/_run-tests-all.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Run all tests. 3 | 4 | @Actions._run-tests-docker 5 | 6 | @log.line 7 | @log.title 'Local tests' 8 | 9 | @Actions._run-tests 10 | 11 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 12 | -------------------------------------------------------------------------------- /src/actions/_run-tests-docker.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Run tests with Docker. 3 | 4 | local docker_images="${@:-ubuntu debian:8}" 5 | 6 | local docker_params='' 7 | 8 | if ${BX_TTY}; then 9 | docker_params='-ti' 10 | fi 11 | 12 | _dkrTest() { 13 | local docker_image="$1" 14 | 15 | @log.line 16 | @log.title "Docker tests: ${docker_image}" 17 | 18 | docker run --rm \ 19 | -v "$(pwd):/root/.bashx/master:ro" \ 20 | -v "$(pwd):/app path:ro" \ 21 | --entrypoint "/app path/bashx" \ 22 | ${docker_params} ${docker_image} '_run-tests' 23 | } 24 | 25 | for img in ${docker_images}; do 26 | _dkrTest ${img} 27 | done 28 | 29 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 30 | -------------------------------------------------------------------------------- /src/actions/_run-tests.xsh: -------------------------------------------------------------------------------- 1 | ## [*] 2 | ## Run tests. 3 | ## 4 | ## Params: 5 | ## *: {String} Tests names to execute separated by a space. 6 | ## If not setted, all tests will be executed. 7 | ## Optional. 8 | 9 | local tests_names_to_execute=($@) 10 | 11 | local error_count=0 12 | local count=0 13 | local test_result 14 | local test_success_flag="$(@mktemp false)" 15 | 16 | @asserts.load 17 | 18 | ############################################################################### 19 | # Tests 20 | 21 | if [[ -d "${BASHX_TESTS_PATH}" ]]; then 22 | for f in "${BASHX_TESTS_PATH}"/*.${BX_SCRIPT_EXTENSION} ; do 23 | if [[ -f "${f}" ]]; then 24 | local src_test_name="$(@file.name "${f}" true)" 25 | 26 | if [[ -z "${tests_names_to_execute}" ]] \ 27 | || @array.contains "${src_test_name}" "${tests_names_to_execute[@]}" 28 | then 29 | @log "$(@style color:yellow)Testing ${src_test_name}..." 30 | count=$((count+1)) 31 | 32 | [ -f "${test_success_flag}" ] && rm -f "${test_success_flag}" 33 | 34 | ( 35 | BX_APP_EXIT=true 36 | . "${f}" 37 | exit_code=$? 38 | touch "${test_success_flag}" || @app.error 39 | exit ${exit_code} 40 | ) 41 | 42 | test_result=$? 43 | 44 | if [[ ${test_result} -eq 0 ]]; then 45 | # Success 46 | @log "$(@style color:green)Success" 47 | else 48 | # Error 49 | error_count=$((error_count+1)) 50 | 51 | if [[ ! -f "${test_success_flag}" ]]; then 52 | @log.warn "Warning: No exit from assert" 53 | fi 54 | 55 | @log.alert "Fail! Exit code: ${test_result}" 56 | fi 57 | 58 | @log 59 | @log.line 60 | @log 61 | fi 62 | fi 63 | done 64 | fi 65 | 66 | ############################################################################### 67 | # Finish 68 | 69 | @log "Tests executed: ${count}" 70 | @log 71 | 72 | if [[ ${error_count} -eq 0 ]]; then 73 | @log 'All tests success!' 74 | exit 0 75 | else 76 | @app.error "${error_count} tests fail!" 77 | fi 78 | 79 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 80 | -------------------------------------------------------------------------------- /src/actions/help.xsh: -------------------------------------------------------------------------------- 1 | ## [-a] 2 | ## Print basic usage (this). 3 | ## 4 | ## Options: 5 | ## -a|--all: Show hidden actions help. 6 | 7 | eval "$(@args 'print_all:-a|--all')" 8 | 9 | local prefix="${BX_SCRIPT_FILE_NAME}" 10 | local line 11 | 12 | @log.title 'Help & Usage' 13 | 14 | # Main 15 | @usage.file "${BX_SCRIPT_FULL_PATH}" "${prefix}" 16 | 17 | # Actions 18 | if [[ -d "${BASHX_ACTIONS_PATH}" ]]; then 19 | for f in "${BASHX_ACTIONS_PATH}"/*.${BX_SCRIPT_EXTENSION} ; do 20 | if [[ -f "${f}" ]]; then 21 | if ${args_print_all} || [[ "$(@file.name "${f}" true)" != _* ]]; then 22 | @usage "${f}" "${prefix}" 23 | fi 24 | fi 25 | done 26 | fi 27 | 28 | # Base Actions 29 | for f in "${BASHX_ACTIONS_PATH}"/*.${BX_SCRIPT_EXTENSION} ; do 30 | if [[ -f "${f}" ]]; then 31 | if ${args_print_all} || [[ "$(@file.name "${f}" true)" != _* ]]; then 32 | @usage "${f}" "${prefix}" 33 | fi 34 | fi 35 | done 36 | 37 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 38 | -------------------------------------------------------------------------------- /src/bootstrap.src: -------------------------------------------------------------------------------- 1 | ############################################ 2 | # # 3 | # BashX # 4 | # # 5 | # Extended Bash Framework. # 6 | # # 7 | # URL: https://github.com/reduardo7/bashx # 8 | # # 9 | # Author: Eduardo Daniel Cuomo # 10 | # eduardo.cuomo.ar@gmail.com # 11 | # reduardo7@gmail.com # 12 | # # 13 | ############################################ 14 | 15 | # ######################################## 16 | # This is the complete bootstrap script, 17 | # without compression. 18 | # ######################################## 19 | 20 | 21 | export LC_CTYPE=C 22 | export LC_ALL=C 23 | export LANG=C 24 | 25 | set -e 26 | 27 | x() { 28 | echo "# Error: $*" >&2 29 | unset -f x 30 | exit 1 31 | } 32 | 33 | [ ! -z "${BASHX_VERSION}" ] || x BASHX_VERSION is required 34 | 35 | export BASHX_DIR="${BASHX_DIR:-${HOME:-/tmp}/.bashx/${BASHX_VERSION}}" 36 | 37 | if [ ! -d "${BASHX_DIR}" ]; then 38 | u="https://raw.githubusercontent.com/reduardo7/bashx/${BASHX_VERSION}/src/setup.sh" 39 | if type wget >/dev/null 2>&1 ; then 40 | sh -c "$(wget -q $u -O -)" || x Can not download using wget 41 | elif type curl >/dev/null 2>&1 ; then 42 | sh -c "$(curl -fsSL $u)" || x Can not download using curl 43 | else 44 | x wget or curl are required. Install wget or curl to continue 45 | fi 46 | fi 47 | 48 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 49 | -------------------------------------------------------------------------------- /src/config.init.src: -------------------------------------------------------------------------------- 1 | # ######################################## 2 | # Initial BashX project configuration 3 | # ######################################## 4 | 5 | ## Core directory (can be overridden before load bootstrap). 6 | ## (Assigned at entrypoint script) 7 | # BASHX_VERSION 8 | 9 | ## Core directory (can be overridden before load bootstrap). 10 | ## (Assigned at `init.sh`) 11 | # BX_DIR 12 | 13 | ## Current script Sources directory. 14 | ## Customizable with: BASHX_SRC_DIR. 15 | export BASHX_SRC_DIR="${BASHX_SRC_DIR:-${BX_SCRIPT_FILE_NAME}.src}" 16 | 17 | ## Configuration file (can be overridden before load). 18 | ## Customizable with: BASHX_APP_CONFIG_FILE. 19 | export BASHX_APP_CONFIG_FILE="${BASHX_APP_CONFIG_FILE:-${BX_SCRIPT_DIR}/.env}" 20 | 21 | ## APP Title. 22 | ## Customizable with: BASHX_APP_CONFIG_FILE. 23 | export BASHX_APP_TITLE="${BASHX_APP_TITLE:-BashX}" 24 | 25 | ## APP Version. 26 | export BASHX_APP_VERSION="${BASHX_APP_VERSION:-1.0}" 27 | 28 | ## Default APP color. 29 | ## See "@style". 30 | export BASHX_APP_COLOR_DEFAULT="${BASHX_APP_COLOR_DEFAULT:-cyan-light}" 31 | 32 | ## Output colors? 33 | ## See "@style". 34 | export BASHX_APP_COLORS_ENABLED=${BASHX_APP_COLORS_ENABLED:-true} 35 | 36 | ## APP columns width. 37 | export BASHX_APP_WIDTH=${BASHX_APP_WIDTH:-80} 38 | 39 | ## Start character for formatted print screen. 40 | ## See "@log". 41 | ${BASHX_APP_COLORS_ENABLED} && _BASHX_APP_PRINT_PREFIX='ℹ️ ' || _BASHX_APP_PRINT_PREFIX='# ' 42 | export BASHX_APP_PRINT_PREFIX="${BASHX_APP_PRINT_PREFIX:-${_BASHX_APP_PRINT_PREFIX}}" 43 | unset _BASHX_APP_PRINT_PREFIX 44 | 45 | ## Default action to call. 46 | ## Action to use if script called without arguments. 47 | export BASHX_APP_DEFAULT_ACTION="${BASHX_APP_DEFAULT_ACTION:-help}" 48 | 49 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 50 | -------------------------------------------------------------------------------- /src/config.src: -------------------------------------------------------------------------------- 1 | # ######################################## 2 | # BashX project configuration 3 | # ######################################## 4 | 5 | ## Prefix for automatic documentation (like this comment). 6 | export BASHX_DOC_MARK='##' 7 | 8 | ## Current script Sources path. Constant. 9 | export BASHX_SRC_PATH="${BX_SCRIPT_DIR}/${BASHX_SRC_DIR}" 10 | 11 | ## Tests path. 12 | export BASHX_TESTS_PATH="${BASHX_SRC_PATH}/tests" 13 | 14 | ## Actions path. 15 | export BASHX_ACTIONS_PATH="${BASHX_SRC_PATH}/actions" 16 | 17 | ## Utils path. 18 | export BASHX_UTILS_PATH="${BASHX_SRC_PATH}/utils" 19 | 20 | ## Events path. 21 | export BASHX_EVENTS_PATH="${BASHX_SRC_PATH}/events" 22 | 23 | ## Resources path. 24 | export BASHX_RESOURCES_PATH="${BASHX_SRC_PATH}/resources" 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/constants.src: -------------------------------------------------------------------------------- 1 | # ######################################## 2 | # Readonly BashX internal constants 3 | # ######################################## 4 | 5 | ## BashX called script arguments. 6 | export BX_SCRIPT_ARGS=($@) 7 | 8 | ## BashX scripts extension. 9 | readonly BX_SCRIPT_EXTENSION='xsh' 10 | 11 | ## Called script. 12 | readonly BX_SCRIPT_CALLED="${BX_SCRIPT_CALLED}" 13 | 14 | ## Current script file name. 15 | readonly BX_SCRIPT_FILE_NAME="${BX_SCRIPT_FILE_NAME}" 16 | 17 | ## Current script full path. 18 | readonly BX_SCRIPT_FULL_PATH="${BX_SCRIPT_DIR}/${BX_SCRIPT_FILE_NAME}" 19 | 20 | ## Current script directory. 21 | readonly BX_SCRIPT_DIR="${BX_SCRIPT_DIR}" 22 | 23 | ## Core actions path. 24 | readonly BX_ACTIONS_PATH="${BX_SRC_PATH}/actions" 25 | 26 | ## Core utils path. 27 | readonly BX_UTILS_PATH="${BX_SRC_PATH}/utils" 28 | 29 | ### OS Constants 30 | 31 | export BX_OS_IS_LINUX=false 32 | export BX_OS_IS_MAC=false 33 | export BX_OS_IS_MINGW=false 34 | 35 | if [[ "$(uname)" == "Darwin" ]]; then 36 | defaults write org.R-project.R force.LANG en_US.UTF-8 37 | export TERM="xterm-color" 38 | export BX_OS_IS_MAC=true 39 | elif [[ "$(expr substr $(uname -s) 1 5)" == "Linux" ]]; then 40 | export BX_OS_IS_LINUX=true 41 | elif [[ "$(expr substr $(uname -s) 1 10)" == MINGW* ]]; then 42 | export BX_OS_IS_MINGW=true 43 | fi 44 | 45 | ## Is Linux OS? 46 | readonly BX_OS_IS_LINUX=${BX_OS_IS_LINUX} 47 | ## Is Mac OS? 48 | readonly BX_OS_IS_MAC=${BX_OS_IS_MAC} 49 | ## Is Win OS? 50 | readonly BX_OS_IS_MINGW=${BX_OS_IS_MINGW} 51 | 52 | BX_BASE_SOURCE="${BASH_SOURCE[0]}" 53 | while [ -h "${BX_BASE_SOURCE}" ]; do 54 | BX_BASE_DIR="$(cd -P "$(dirname "${BX_BASE_SOURCE}")" && pwd)" 55 | BX_BASE_SOURCE=`readlink "${BX_BASE_SOURCE}"` 56 | [[ "${BX_BASE_SOURCE}" != /* ]] && BX_BASE_SOURCE="${BX_BASE_DIR}/${BX_BASE_SOURCE}" 57 | done 58 | [ -z "${BX_BASE_DIR}"] && BX_BASE_DIR="$(cd -P "$(dirname "${BX_BASE_SOURCE}")" && pwd)" 59 | 60 | ## BashX base source path. 61 | readonly BX_BASE_SOURCE="${BX_BASE_SOURCE}" 62 | 63 | # BashX base directory path. 64 | readonly BX_BASE_DIR="${BX_BASE_DIR}" 65 | 66 | # \e | \033 | \x1B 67 | ## Key: ESC 68 | ${BX_OS_IS_MAC} && readonly BX_KEY_ESC=$'\x1B' || readonly BX_KEY_ESC=$'\e' 69 | 70 | # https://stackoverflow.com/a/911213/717267 71 | ## TTY available? 72 | [ -t 1 ] && readonly BX_TTY=true || readonly BX_TTY=false 73 | 74 | # \n - New Line 75 | ## New Line constant (\n) 76 | readonly BX_CHAR_NL=' 77 | ' 78 | 79 | # \t - Tab 80 | ## New Line constant (\t) 81 | readonly BX_CHAR_TAB=' ' 82 | 83 | ## Action prefix. Used at @app.run. 84 | readonly BX_ACTION_PREFIX='@Actions' 85 | 86 | ## Events options. 87 | readonly BX_EVENTS_OPTS=(invalid-action ready start error finish) 88 | 89 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 90 | -------------------------------------------------------------------------------- /src/init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################ 4 | # # 5 | # BashX # 6 | # # 7 | # Extended Bash Framework # 8 | # # 9 | # URL: https://github.com/reduardo7/bashx # 10 | # # 11 | # Author: Eduardo Daniel Cuomo # 12 | # eduardo.cuomo.ar@gmail.com # 13 | # reduardo7@gmail.com # 14 | # # 15 | ############################################ 16 | 17 | set +e 18 | 19 | export LC_CTYPE=C 20 | export LC_ALL=C 21 | export LANG=C 22 | 23 | BX_SCRIPT_CALLED="$0" 24 | BX_SCRIPT_DIR="$(cd "$(dirname "${BX_SCRIPT_CALLED}")" ; pwd)" 25 | BX_SCRIPT_FILE_NAME="$(basename "${BX_SCRIPT_CALLED}")" 26 | while [ -h "${BX_SCRIPT_CALLED}" ]; do 27 | BX_SCRIPT_CALLED="$(readlink "${BX_SCRIPT_CALLED}")" 28 | BX_SCRIPT_DIR="$(cd "${BX_SCRIPT_DIR}" ; cd "$(dirname "${BX_SCRIPT_CALLED}")" ; pwd)" 29 | BX_SCRIPT_FILE_NAME="$(basename "${BX_SCRIPT_CALLED}")" 30 | BX_SCRIPT_CALLED="$(cd "${BX_SCRIPT_DIR}" ; pwd)/${BX_SCRIPT_FILE_NAME}" 31 | done 32 | 33 | # Test and force run with "bash" interpreter 34 | if [ -z "${BASH}" ]; then 35 | bash "$(basename "${BX_SCRIPT_CALLED}")" "$@" 36 | exit $? 37 | fi 38 | 39 | ## `BASHX_VERSION` constant should be defined before load the bootstrap. 40 | readonly BASHX_VERSION="${BASHX_VERSION}" 41 | [ ! -z "${BASHX_VERSION}" ] || { 42 | echo '"BASHX_VERSION" is required!' >&2 43 | exit 1 44 | } 45 | 46 | # ############################################################################# 47 | 48 | ### Ensure environment constants 49 | 50 | [[ ! -z "${UID}" ]] || export UID=$(id -u) 51 | [[ ! -z "${GID}" ]] || export GID=$(id -g) 52 | 53 | ## Core directory (can be overridden before load with BX_DIR). 54 | readonly BX_DIR="${BX_DIR:-${HOME:-/tmp}/.bashx/${BASHX_VERSION}}" 55 | [ -d "${BX_DIR}" ] || { 56 | echo "[BX_DIR=${BX_DIR}] is invelid!" >&2 57 | exit 2 58 | } 59 | 60 | ## Core SRC path. 61 | readonly BX_SRC_PATH="${BX_DIR}/src" 62 | 63 | . "${BX_SRC_PATH}/config.init.src" 64 | . "${BX_SRC_PATH}/constants.src" 65 | . "${BX_SRC_PATH}/config.src" 66 | 67 | ### Load config 68 | 69 | if [[ -f "${BASHX_APP_CONFIG_FILE}" ]]; then 70 | . "${BASHX_APP_CONFIG_FILE}" 71 | fi 72 | 73 | # ############################################################################# 74 | 75 | ### VARS 76 | 77 | ## APP Temporary path. 78 | readonly BASHX_APP_TMP_PATH="${BASHX_APP_TMP_PATH:-$(mktemp -d)}" 79 | 80 | ## APP main process index. 81 | readonly BX_PROC_INDEX_MAIN=${BASH_SUBSHELL} 82 | 83 | echo ${BX_PROC_INDEX_MAIN} > "${BASHX_APP_TMP_PATH}/__bx_shell_main" 84 | 85 | # ############################################################################# 86 | 87 | ### PRIVATE VARS 88 | 89 | ## Current called action. 90 | export BX_ACTION='' 91 | 92 | ## True if APP is terminated with illegal error. 93 | BX_APP_EXIT_ILLEGAL_ERROR=false 94 | 95 | # Events commands 96 | BX_ON_EXIT='' 97 | # BX_ON_STDOUT='' 98 | # BX_ON_STDERR='' 99 | # BX_ON_STDINFO='' 100 | 101 | # True if APP is terminated 102 | BX_APP_EXIT=false 103 | 104 | # ############################################################################# 105 | 106 | # info out > std err 107 | exec 3>&2 108 | 109 | # Debug (set +x) prompt 110 | ${BASHX_APP_COLORS_ENABLED} && PS4=' 🐛 ' || PS4='> % ' 111 | 112 | # ############################################################################# 113 | 114 | ### Preload required 115 | 116 | __BX_each_line_=true 117 | 118 | # Internal checks for sub-shells 119 | __BX_each_line() { 120 | if \ 121 | ${__BX_each_line_} \ 122 | && ! ${BX_APP_EXIT} \ 123 | && [[ ${BX_PROC_INDEX_MAIN} == ${BASH_SUBSHELL} ]] \ 124 | && [ -f "${BASHX_APP_TMP_PATH}/__bx_subshell_exit" ] 125 | then 126 | __BX_each_line_=false 127 | exit $(cat "${BASHX_APP_TMP_PATH}/__bx_subshell_exit") 128 | fi 129 | } 130 | 131 | @str.trim() { . "${BX_UTILS_PATH}/str/trim.${BX_SCRIPT_EXTENSION}"; } 132 | @str.toLower() { . "${BX_UTILS_PATH}/str/toLower.${BX_SCRIPT_EXTENSION}"; } 133 | @style() { . "${BX_UTILS_PATH}/style.${BX_SCRIPT_EXTENSION}"; } 134 | @file.name() { . "${BX_UTILS_PATH}/file/name.${BX_SCRIPT_EXTENSION}"; } 135 | @function.load() { . "${BX_UTILS_PATH}/function/load.${BX_SCRIPT_EXTENSION}"; } 136 | 137 | # ############################################################################# 138 | 139 | ### Bootstrap 140 | 141 | # Load base 142 | @function.load "${BX_UTILS_PATH}" '@' 143 | @function.load "${BX_ACTIONS_PATH}" "${BX_ACTION_PREFIX}." 144 | 145 | # Load custom 146 | [ ! -d "${BASHX_UTILS_PATH}" ] || @function.load "${BASHX_UTILS_PATH}" '@' 147 | [ ! -d "${BASHX_ACTIONS_PATH}" ] || @function.load "${BASHX_ACTIONS_PATH}" "${BX_ACTION_PREFIX}." 148 | 149 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 150 | -------------------------------------------------------------------------------- /src/setup.sh: -------------------------------------------------------------------------------- 1 | ############################################ 2 | # # 3 | # BashX # 4 | # # 5 | # Extended Bash Framework. # 6 | # # 7 | # URL: https://github.com/reduardo7/bashx # 8 | # # 9 | # Author: Eduardo Daniel Cuomo # 10 | # eduardo.cuomo.ar@gmail.com # 11 | # reduardo7@gmail.com # 12 | # # 13 | ############################################ 14 | 15 | export LC_CTYPE=C 16 | export LC_ALL=C 17 | export LANG=C 18 | 19 | _e() { 20 | echo "# $@" 21 | } 22 | 23 | _x() { 24 | _e "Error: $@" >&2 25 | exit 1 26 | } 27 | 28 | [ -z "${BASHX_VERSION}" ] && _x BASHX_VERSION is required 29 | type tar >/dev/null 2>&1 || _x tar is required. Install tar to continue 30 | export BASHX_DIR="${BASHX_DIR:-${HOME:-/tmp}/.bashx/$BASHX_VERSION}" 31 | 32 | if [ ! -d "${BASHX_DIR}" ]; then 33 | export t="$(mktemp -d)" 34 | 35 | # Install 36 | ( 37 | set -e 38 | _e Installing BashX ${BASHX_VERSION}... 39 | 40 | m="Error downloading BashX ${BASHX_VERSION}" 41 | g="https://github.com/reduardo7/bashx/tarball/${BASHX_VERSION}" 42 | 43 | cd "$t" 44 | 45 | if type wget >/dev/null 2>&1 ; then 46 | wget -q $g -O - | tar -xz || _x $m 47 | elif type curl >/dev/null 2>&1 ; then 48 | curl -sL $g | tar -xz || _x $m 49 | else 50 | _x wget or curl are required. Install wget or curl to continue 51 | fi 52 | 53 | mkdir -p "${BASHX_DIR}" 54 | rm -rf "${BASHX_DIR}" 55 | mv reduardo7-bashx-* "${BASHX_DIR}" 56 | ) 57 | e=$? 58 | 59 | # Cleanup 60 | rm -rf "$t" >/dev/null 2>&1 || true 61 | 62 | # Finish 63 | exit $e 64 | fi 65 | 66 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 67 | -------------------------------------------------------------------------------- /src/tests/000.asserts.xsh: -------------------------------------------------------------------------------- 1 | @@assert.exec "@@assert.fail" false 2 | @@assert.exec "@@assert.fail" false '"asd"' 3 | 4 | @@assert.exec "@@assert.true" true true 5 | @@assert.exec "@@assert.true" false false 6 | @@assert.exec "@@assert.true" false '"x"' 7 | 8 | @@assert.exec "@@assert.false" true false 9 | @@assert.exec "@@assert.false" false true 10 | @@assert.exec "@@assert.false" false '"x"' 11 | 12 | @@assert.exec "@@assert.empty" true '""' 13 | @@assert.exec "@@assert.empty" false '"x"' 14 | @@assert.exec "@@assert.empty" false '"${BX_KEY_ESC}"' 15 | @@assert.exec "@@assert.empty" false '" "' 16 | @@assert.exec "@@assert.empty" false '"\t"' 17 | @@assert.exec "@@assert.empty" false '"\n"' 18 | 19 | @@assert.exec "@@assert.notEmpty" true 'x' 20 | @@assert.exec "@@assert.notEmpty" true '" "' 21 | @@assert.exec "@@assert.notEmpty" true '"\t"' 22 | @@assert.exec "@@assert.notEmpty" true '"\n"' 23 | @@assert.exec "@@assert.notEmpty" true '"${BX_KEY_ESC}"' 24 | @@assert.exec "@@assert.notEmpty" false '""' 25 | 26 | @@assert.exec "@@assert.number" true '1' 27 | @@assert.exec "@@assert.number" true '0' 28 | @@assert.exec "@@assert.number" true '00' 29 | @@assert.exec "@@assert.number" true '10' 30 | @@assert.exec "@@assert.number" true '5' 31 | @@assert.exec "@@assert.number" false '-1' 32 | @@assert.exec "@@assert.number" false '""' 33 | @@assert.exec "@@assert.number" false '" "' 34 | @@assert.exec "@@assert.number" false '" 1"' 35 | @@assert.exec "@@assert.number" false '"1 "' 36 | @@assert.exec "@@assert.number" false '"1 1"' 37 | @@assert.exec "@@assert.number" false 'x' 38 | 39 | @@assert.exec "@@assert.errorCode" true '1' 40 | @@assert.exec "@@assert.errorCode" true '10' 41 | @@assert.exec "@@assert.errorCode" true '255' 42 | @@assert.exec "@@assert.errorCode" false '256' 43 | @@assert.exec "@@assert.errorCode" false '300' 44 | @@assert.exec "@@assert.errorCode" false '0' 45 | @@assert.exec "@@assert.errorCode" false '""' 46 | @@assert.exec "@@assert.errorCode" false '" "' 47 | @@assert.exec "@@assert.errorCode" false 'x' 48 | @@assert.exec "@@assert.errorCode" false '"1 "' 49 | @@assert.exec "@@assert.errorCode" false '" 1"' 50 | @@assert.exec "@@assert.errorCode" false '"1 1"' 51 | 52 | @@assert.exec "@@assert.notErrorCode" true '0' 53 | @@assert.exec "@@assert.notErrorCode" false '1' 54 | @@assert.exec "@@assert.notErrorCode" false '10' 55 | @@assert.exec "@@assert.notErrorCode" false '255' 56 | @@assert.exec "@@assert.notErrorCode" false '300' 57 | @@assert.exec "@@assert.notErrorCode" false '""' 58 | @@assert.exec "@@assert.notErrorCode" false '" "' 59 | @@assert.exec "@@assert.notErrorCode" false 'x' 60 | @@assert.exec "@@assert.notErrorCode" false '"1 "' 61 | @@assert.exec "@@assert.notErrorCode" false '" 1"' 62 | @@assert.exec "@@assert.notErrorCode" false '"1 1"' 63 | 64 | @@assert.exec "@@assert.equal" true '1 1' 65 | @@assert.exec "@@assert.equal" true 'a a' 66 | @@assert.exec "@@assert.equal" true '0 0' 67 | @@assert.exec "@@assert.equal" true '"" ""' 68 | @@assert.exec "@@assert.equal" true '"${BX_KEY_ESC}" "${BX_KEY_ESC}"' 69 | @@assert.exec "@@assert.equal" false 'a A' 70 | @@assert.exec "@@assert.equal" false 'A a' 71 | @@assert.exec "@@assert.equal" false '"" 0' 72 | 73 | @@assert.exec "@@assert.notEqual" true 'a A' 74 | @@assert.exec "@@assert.notEqual" true 'A a' 75 | @@assert.exec "@@assert.notEqual" true '"" 0' 76 | @@assert.exec "@@assert.notEqual" false '1 1' 77 | @@assert.exec "@@assert.notEqual" false 'a a' 78 | @@assert.exec "@@assert.notEqual" false '0 0' 79 | @@assert.exec "@@assert.notEqual" false '"" ""' 80 | @@assert.exec "@@assert.notEqual" false '"${BX_KEY_ESC}" "${BX_KEY_ESC}"' 81 | 82 | @@assert.exec "@@assert.contains" true 'abc b' 83 | @@assert.exec "@@assert.contains" true 'abc a' 84 | @@assert.exec "@@assert.contains" true 'abc c' 85 | @@assert.exec "@@assert.contains" true 'abc bc' 86 | @@assert.exec "@@assert.contains" true 'abc abc' 87 | @@assert.exec "@@assert.contains" false '"" ""' 88 | @@assert.exec "@@assert.contains" false 'abc x' 89 | @@assert.exec "@@assert.contains" false 'abc A' 90 | @@assert.exec "@@assert.contains" false 'abc ""' 91 | @@assert.exec "@@assert.contains" false 'abc B' 92 | 93 | @@assert.exec "@@assert.notContains" true 'abc x' 94 | @@assert.exec "@@assert.notContains" true 'abc A' 95 | @@assert.exec "@@assert.notContains" true 'abc B' 96 | @@assert.exec "@@assert.notContains" false 'abc ""' 97 | @@assert.exec "@@assert.notContains" false '"" ""' 98 | @@assert.exec "@@assert.notContains" false 'abc b' 99 | @@assert.exec "@@assert.notContains" false 'abc a' 100 | @@assert.exec "@@assert.notContains" false 'abc c' 101 | @@assert.exec "@@assert.notContains" false 'abc bc' 102 | @@assert.exec "@@assert.notContains" false 'abc abc' 103 | 104 | @@assert.exec "@@assert.stdOut" true '"echo asd"' 105 | @@assert.exec "@@assert.stdOut" true '"echo \"${BX_KEY_ESC}\""' 106 | @@assert.exec "@@assert.stdOut" false '"echo asd >&2"' 107 | @@assert.exec "@@assert.stdOut" false '"echo"' 108 | 109 | @@assert.exec "@@assert.noStdOut" true '"echo asd >&2"' 110 | @@assert.exec "@@assert.noStdOut" true '"echo"' 111 | @@assert.exec "@@assert.noStdOut" false '"echo asd"' 112 | @@assert.exec "@@assert.noStdOut" false '"echo \"${BX_KEY_ESC}\""' 113 | 114 | @@assert.exec "@@assert.errOut" true '"echo asd >&2"' 115 | @@assert.exec "@@assert.errOut" true '"echo \"${BX_KEY_ESC}\" >&2"' 116 | @@assert.exec "@@assert.errOut" false '"echo"' 117 | @@assert.exec "@@assert.errOut" false '"echo asd"' 118 | 119 | @@assert.exec "@@assert.noErrOut" true '"echo"' 120 | @@assert.exec "@@assert.noErrOut" true '"echo asd"' 121 | @@assert.exec "@@assert.noErrOut" false '"echo asd >&2"' 122 | @@assert.exec "@@assert.noErrOut" false '"echo \"${BX_KEY_ESC}\" >&2"' 123 | 124 | @@assert.exec "@@assert.noOut" true '"echo"' 125 | @@assert.exec "@@assert.noOut" false '"echo asd"' 126 | @@assert.exec "@@assert.noOut" false '"echo asd >&2"' 127 | @@assert.exec "@@assert.noOut" false '"echo asd >&3"' 128 | @@assert.exec "@@assert.noOut" false '"echo \"${BX_KEY_ESC}\""' 129 | @@assert.exec "@@assert.noOut" false '"echo \"${BX_KEY_ESC}\" >&2"' 130 | @@assert.exec "@@assert.noOut" false '"echo \"${BX_KEY_ESC}\" >&3"' 131 | 132 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 133 | -------------------------------------------------------------------------------- /src/tests/README.md: -------------------------------------------------------------------------------- 1 | # BashX Tests 2 | 3 | Available _BashX_ **tests**. 4 | 5 | You can run available **tests** with [`./bashx _run-tests`](../../bashx). 6 | -------------------------------------------------------------------------------- /src/tests/app.info.xsh: -------------------------------------------------------------------------------- 1 | result="$(@app.info)" 2 | 3 | @@assert.notEmpty "${result}" 4 | @@assert.contains "${result}" "${BASHX_APP_TITLE}" 5 | @@assert.contains "${result}" "v${BASHX_APP_VERSION}" 6 | 7 | @@assert.stdOut "@app.info 'output this'" 8 | @@assert.noErrOut "@app.info 'no output this'" 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/tests/args.xsh: -------------------------------------------------------------------------------- 1 | result="$(eval "$(@args 'new:-n|-N' 'path:-p|--path:true' 'foo:-f')")" 2 | @@assert.noOut "${result}" 3 | 4 | testFunc() { 5 | eval "$(@args 'new:-n|-N' 'path:-p|--path:true' 'foo:-f')" 6 | echo $1 7 | } 8 | 9 | @@assert.equal '' "$(testFunc -n)" 10 | @@assert.equal 'asd' "$(testFunc -n asd)" 11 | @@assert.equal 'n' "$(testFunc n one)" 12 | @@assert.equal 'asd' "$(testFunc asd)" 13 | @@assert.equal 'asd' "$(testFunc -f asd)" 14 | @@assert.equal 'asd' "$(testFunc -p 123 asd)" 15 | @@assert.equal 'asd' "$(testFunc -p 123 --path querty asd)" 16 | @@assert.equal '' "$(testFunc --path asd)" 17 | @@assert.equal '' "$(testFunc --path asd -N)" 18 | @@assert.equal 'asd' "$(testFunc --path asd -N asd)" 19 | 20 | testFuncResult() { 21 | eval "$(@args 'new:-n|-N' 'path:-p|--path:true' 'foo:-f')" 22 | eval "echo \${$1}" 23 | } 24 | 25 | @@assert.equal 'true' "$(testFuncResult -n args_new)" 26 | @@assert.equal 'true' "$(testFuncResult -N args_new)" 27 | @@assert.equal 'false' "$(testFuncResult args_new)" 28 | @@assert.equal 'false' "$(testFuncResult -f args_new)" 29 | @@assert.equal 'false' "$(testFuncResult -p asd args_new)" 30 | 31 | @@assert.equal 'false' "$(testFuncResult args_foo)" 32 | @@assert.equal 'true' "$(testFuncResult -f args_foo)" 33 | @@assert.equal 'false' "$(testFuncResult -p asd args_foo)" 34 | 35 | @@assert.equal '' "$(testFuncResult 'args_path[@]')" 36 | @@assert.equal 'aaa' "$(testFuncResult -p aaa 'args_path[@]')" 37 | @@assert.equal '1' "$(testFuncResult -p aaa '#args_path[@]')" 38 | @@assert.equal 'aaa bbb ccc' "$(testFuncResult -p aaa -p bbb --path ccc -n 'args_path[@]')" 39 | @@assert.equal '3' "$(testFuncResult -p aaa -p bbb -N --path ccc '#args_path[@]')" 40 | 41 | @app.error() { 42 | echo 'ERROR-OK' 43 | exit 1 44 | } 45 | @@assert.equal "$(@app.error)" "$(testFunc -invalid)" 46 | 47 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 48 | -------------------------------------------------------------------------------- /src/tests/array.contains.xsh: -------------------------------------------------------------------------------- 1 | array=("abc" "aaa bbb" "123" "aaa" "aaa 123 bbb" "a 5 b" "" true false 123 0) 2 | 3 | @@assert.noOut '@array.contains' 4 | @@assert.noOut '@array.contains "a"' 5 | @@assert.noOut '@array.contains "a" "b"' 6 | @@assert.noOut '@array.contains "a" "a"' 7 | @@assert.noOut '@array.contains "" "b"' 8 | @@assert.noOut '@array.contains "abc" "${array[@]}"' 9 | @@assert.noOut '@array.contains "" "${array[@]}"' 10 | @@assert.noOut '@array.contains true "${array[@]}"' 11 | @@assert.noOut '@array.contains 0 "${array[@]}"' 12 | 13 | @@assert.exec '@array.contains' true '"abc" "${array[@]}"' 14 | @@assert.exec '@array.contains' true 'true "${array[@]}"' 15 | @@assert.exec '@array.contains' true '0 "${array[@]}"' 16 | @@assert.exec '@array.contains' true 'false "${array[@]}"' 17 | @@assert.exec '@array.contains' false '5 "${array[@]}"' 18 | @@assert.exec '@array.contains' false '"5" "${array[@]}"' 19 | @@assert.exec '@array.contains' false '"bbb" "${array[@]}"' 20 | @@assert.exec '@array.contains' true '"aaa bbb" "${array[@]}"' 21 | @@assert.exec '@array.contains' true '"0" "${array[@]}"' 22 | @@assert.exec '@array.contains' false '"123 bbb" "${array[@]}"' 23 | 24 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 25 | -------------------------------------------------------------------------------- /src/tests/checkError.xsh: -------------------------------------------------------------------------------- 1 | str='asdfg' 2 | 3 | @@assert.noOut "@checkError 0 'echo ${str}'" 4 | @@assert.stdOut "@checkError 1 'echo ${str}'" 5 | 6 | result="$(@checkError 0 "echo ${str}")" 7 | @@assert.empty "${result}" 8 | 9 | result="$(@checkError 1 "echo ${str}")" 10 | @@assert.equal "${str}" "${result}" 11 | 12 | result="$(@checkError 200 "echo ${str}")" 13 | @@assert.equal "${str}" "${result}" 14 | 15 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 16 | -------------------------------------------------------------------------------- /src/tests/file.contains.xsh: -------------------------------------------------------------------------------- 1 | file="$(@mktemp)" 2 | 3 | echo foo >${file} 4 | echo bar >>${file} 5 | echo goo >>${file} 6 | 7 | result="$(@file.contains 'bar' "${file}")" 8 | @@assert.noOut "${result}" 9 | 10 | @file.contains 'bar' "${file}" 11 | @@assert.notErrorCode $? 12 | 13 | @file.contains 'xx' "${file}" 14 | @@assert.errorCode $? 15 | 16 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 17 | -------------------------------------------------------------------------------- /src/tests/file.name.xsh: -------------------------------------------------------------------------------- 1 | @@assert.equal "$(@file.name 'foo.bar')" 'foo.bar' 2 | @@assert.equal "$(@file.name 'foo.bar' false)" 'foo.bar' 3 | @@assert.equal "$(@file.name 'foo.bar' true)" 'foo' 4 | @@assert.equal "$(@file.name '/tmp/foo.bar.xx')" 'foo.bar.xx' 5 | @@assert.equal "$(@file.name '/a.b/foo')" 'foo' 6 | @@assert.equal "$(@file.name '/tmp/foo.bar.xx' true)" 'foo.bar' 7 | @@assert.equal "$(@file.name '/tmp/.foo.bar' true)" '.foo' 8 | @@assert.equal "$(@file.name '/tmp/.foo.bar.xx' true)" '.foo.bar' 9 | 10 | @@assert.noErrOut '@file.name "/tmp/foo.bar.xx"' 11 | 12 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 13 | -------------------------------------------------------------------------------- /src/tests/function.exists.xsh: -------------------------------------------------------------------------------- 1 | testFunc() { 2 | echo foo 3 | } 4 | 5 | @@assert.noOut "@function.exists xx" 6 | @@assert.noOut "@function.exists echo" 7 | @@assert.noOut "@function.exists testFunc" 8 | @@assert.exec '@function.exists' false 'xx' 9 | @@assert.exec '@function.exists' false 'echo' 10 | @@assert.exec '@function.exists' true 'testFunc' 11 | 12 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 13 | -------------------------------------------------------------------------------- /src/tests/function.void.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noOut '@function.void' 2 | @@assert.noOut '@function.void "a" "b"' 3 | 4 | code="$(@function.void ; echo $?)" 5 | @@assert.notErrorCode $code 6 | 7 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 8 | -------------------------------------------------------------------------------- /src/tests/isNumber.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noOut '@isNumber' 2 | @@assert.noOut '@isNumber 0' 3 | @@assert.noOut '@isNumber asd' 4 | @@assert.noOut '@isNumber ""' 5 | @@assert.noOut '@isNumber " "' 6 | 7 | @@assert.exec '@isNumber' true '0' 8 | @@assert.exec '@isNumber' true '1' 9 | @@assert.exec '@isNumber' true '01' 10 | @@assert.exec '@isNumber' true '10' 11 | @@assert.exec '@isNumber' true '99' 12 | 13 | 14 | @@assert.exec '@isNumber' false '' 15 | @@assert.exec '@isNumber' false 'a' 16 | @@assert.exec '@isNumber' false '" 0"' 17 | @@assert.exec '@isNumber' false '"0 "' 18 | @@assert.exec '@isNumber' false '" 0 "' 19 | @@assert.exec '@isNumber' false '"1 0"' 20 | @@assert.exec '@isNumber' false '""' 21 | 22 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 23 | -------------------------------------------------------------------------------- /src/tests/isRoot.xsh: -------------------------------------------------------------------------------- 1 | if [[ "$(id -u)" -ne 0 ]]; then 2 | # No Root 3 | is_root=false 4 | else 5 | # Root 6 | is_root=true 7 | fi 8 | 9 | @@assert.noOut '@isRoot' 10 | 11 | @@assert.exec '@isRoot' ${is_root} 12 | 13 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 14 | -------------------------------------------------------------------------------- /src/tests/log.alert.xsh: -------------------------------------------------------------------------------- 1 | str="my test string" 2 | style_color_red="$(@style color:red)" 3 | result="$(@log.alert "${str}" 2>&1)" 4 | 5 | @@assert.notEmpty "${result}" 6 | @@assert.contains "${result}" "${style_color_red}" 7 | @@assert.contains "${result}" "${str}" 8 | 9 | @@assert.noStdOut "@log.alert 'no output this'" 10 | @@assert.errOut "@log.alert 'output this'" 11 | 12 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 13 | -------------------------------------------------------------------------------- /src/tests/log.cmd.xsh: -------------------------------------------------------------------------------- 1 | script='fnx() { local x="x" ; echo "${x}a${x}" ; } ; fnx' 2 | output="$(@log.cmd "${script}" 3>/dev/null 2>/dev/null)" 3 | @@assert.contains "${output}" 'xax' 4 | 5 | output="$(var=123 ; fnx() { c="var=456 ; echo [${1}]" ; echo "$c" ; eval "$c" ; } ; @log.cmd fnx "\\\$var" 3>/dev/null 2>/dev/null)" 6 | @@assert.contains "${output}" '[456]' 7 | 8 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 9 | -------------------------------------------------------------------------------- /src/tests/log.warn.xsh: -------------------------------------------------------------------------------- 1 | str="my test string" 2 | result="$(@log.warn "${str}" 2>&1)" 3 | 4 | @@assert.notEmpty "${result}" 5 | @@assert.contains "${result}" "${str}" 6 | 7 | @@assert.noStdOut "@log.warn 'no output this'" 8 | @@assert.errOut "@log.warn 'output this'" 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/tests/log.xsh: -------------------------------------------------------------------------------- 1 | output="$(@log 'no print' 3>/dev/null)" 2 | @@assert.empty "${output}" 3 | 4 | output="$(@log "foo\tbar" 3>&1)" 5 | @@assert.notEmpty "${output}" 6 | @@assert.notContains "${output}" "foo bar" 7 | @@assert.contains "${output}" 'foo' 8 | @@assert.contains "${output}" 'bar' 9 | @@assert.contains "${output}" "${BASHX_APP_PRINT_PREFIX}" 10 | # @@assert.endWith "${output}" "$(@style reset)" 11 | 12 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 13 | -------------------------------------------------------------------------------- /src/tests/random.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@random' 2 | @@assert.stdOut '@random' 3 | 4 | @@assert.regExp '^[a-zA-Z0-9]+$' "$(@random)" 5 | 6 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 7 | -------------------------------------------------------------------------------- /src/tests/removeFormat.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@style.clean "asd"' 2 | 3 | @@assert.equal "$(echo -e "\t foo\nbar \t")" "$(@style.clean "$(echo -e "\t foo\nbar \t")")" 4 | @@assert.equal " yellow red " "$(@style.clean " $(@style color:yellow)yellow $(@style color:red)red ")" 5 | 6 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 7 | -------------------------------------------------------------------------------- /src/tests/screen.width.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@screen.width' 2 | @@assert.stdOut '@screen.width' 3 | 4 | sw="$(@screen.width)" 5 | 6 | @@assert.number "${sw}" 7 | 8 | [[ ${sw} -gt 0 ]] || @@assert.fail "Screen Width: ${sw} <= 0" 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/tests/str.in.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noOut "@str.in 'abc' 'b'" 2 | @@assert.noOut "@str.in 'abc' 'x'" 3 | @@assert.noOut "@str.in 'abc' 'B' true" 4 | @@assert.noOut "@str.in 'abc' 'b' true" 5 | @@assert.noOut "@str.in 'abc' 'B' false" 6 | @@assert.noOut "@str.in 'abc' 'b' false" 7 | 8 | @@assert.exec '@str.in' true '"abc" "a"' 9 | @@assert.exec '@str.in' true '"abc" "b"' 10 | @@assert.exec '@str.in' true '"abc" "c"' 11 | @@assert.exec '@str.in' true '"abc" "abc"' 12 | @@assert.exec '@str.in' true '"abc" "ab"' 13 | @@assert.exec '@str.in' true '"abc" "bc"' 14 | 15 | 16 | @@assert.exec '@str.in' false '"abc" "A"' 17 | @@assert.exec '@str.in' false '"abc" "B"' 18 | @@assert.exec '@str.in' false '"abc" "C"' 19 | @@assert.exec '@str.in' false '"abc" "aBc"' 20 | @@assert.exec '@str.in' false '"abc" "aB"' 21 | @@assert.exec '@str.in' false '"abc" "bC"' 22 | 23 | @@assert.exec '@str.in' false '"ABC" "a"' 24 | @@assert.exec '@str.in' false '"ABC" "b"' 25 | @@assert.exec '@str.in' false '"ABC" "c"' 26 | @@assert.exec '@str.in' false '"ABC" "abc"' 27 | @@assert.exec '@str.in' false '"ABC" "ab"' 28 | @@assert.exec '@str.in' false '"ABC" "bc"' 29 | 30 | 31 | @@assert.exec '@str.in' true '"abc" "a" true' 32 | @@assert.exec '@str.in' true '"abc" "b" true' 33 | @@assert.exec '@str.in' true '"abc" "c" true' 34 | @@assert.exec '@str.in' true '"abc" "abc" true' 35 | @@assert.exec '@str.in' true '"abc" "ab" true' 36 | @@assert.exec '@str.in' true '"abc" "bc" true' 37 | 38 | @@assert.exec '@str.in' false '"abc" "A" true' 39 | @@assert.exec '@str.in' false '"abc" "B" true' 40 | @@assert.exec '@str.in' false '"abc" "C" true' 41 | @@assert.exec '@str.in' false '"abc" "aBc" true' 42 | @@assert.exec '@str.in' false '"abc" "aB" true' 43 | @@assert.exec '@str.in' false '"abc" "bC" true' 44 | 45 | @@assert.exec '@str.in' false '"ABC" "a" true' 46 | @@assert.exec '@str.in' false '"ABC" "b" true' 47 | @@assert.exec '@str.in' false '"ABC" "c" true' 48 | @@assert.exec '@str.in' false '"ABC" "abc" true' 49 | @@assert.exec '@str.in' false '"ABC" "ab" true' 50 | @@assert.exec '@str.in' false '"ABC" "bc" true' 51 | 52 | 53 | @@assert.exec '@str.in' true '"abc" "a" false' 54 | @@assert.exec '@str.in' true '"abc" "b" false' 55 | @@assert.exec '@str.in' true '"abc" "c" false' 56 | @@assert.exec '@str.in' true '"abc" "abc" false' 57 | @@assert.exec '@str.in' true '"abc" "ab" false' 58 | @@assert.exec '@str.in' true '"abc" "bc" false' 59 | 60 | @@assert.exec '@str.in' true '"abc" "A" false' 61 | @@assert.exec '@str.in' true '"abc" "B" false' 62 | @@assert.exec '@str.in' true '"abc" "C" false' 63 | @@assert.exec '@str.in' true '"abc" "aBc" false' 64 | @@assert.exec '@str.in' true '"abc" "aB" false' 65 | @@assert.exec '@str.in' true '"abc" "bC" false' 66 | 67 | @@assert.exec '@str.in' true '"ABC" "a" false' 68 | @@assert.exec '@str.in' true '"ABC" "b" false' 69 | @@assert.exec '@str.in' true '"ABC" "c" false' 70 | @@assert.exec '@str.in' true '"ABC" "abc" false'@assert 71 | @@assert.exec '@str.in' true '"ABC" "ab" false' 72 | @@assert.exec '@str.in' true '"ABC" "bc" false' 73 | 74 | 75 | @@assert.exec '@str.in' false '"abc" "x"' 76 | @@assert.exec '@str.in' false '"abc" "x" true' 77 | @@assert.exec '@str.in' false '"abc" "x" false' 78 | 79 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 80 | -------------------------------------------------------------------------------- /src/tests/str.len.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.len' 2 | @@assert.noErrOut '@str.len "a"' 3 | 4 | sl="$(@str.len "$(@style color:blue)abc$(@style color:default) ")" 5 | 6 | @@assert.number "${sl}" 7 | 8 | [[ ${sl} -eq 4 ]] || @@assert.fail "str.len: ${sl} != 4" 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/tests/str.ltrim.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.ltrim' 2 | @@assert.noErrOut '@str.ltrim a' 3 | 4 | @@assert.equal 'a b1 ' "$(@str.ltrim ' a b1 ')" 5 | @@assert.equal 'a b2 ' "$(@str.ltrim ' a b2 ')" 6 | @@assert.equal ' a x b4 xxx' "$(@str.ltrim 'xxx a x b4 xxx' 'x')" 7 | @@assert.equal 'XXX a x b5 XXX' "$(@str.ltrim 'XXX a x b5 XXX' 'x')" 8 | 9 | @@assert.equal ' ab ab' "$(@str.ltrim 'ab ab ab' 'ab')" 10 | @@assert.equal 'ab' "$(@str.ltrim 'ab ab ab' 'ab ')" 11 | @@assert.equal 'baab' "$(@str.ltrim 'abbaab' 'ab')" 12 | 13 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 14 | -------------------------------------------------------------------------------- /src/tests/str.pos.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.pos' 2 | @@assert.noErrOut '@str.pos "a"' 3 | @@assert.noErrOut '@str.pos "a" "b"' 4 | @@assert.noErrOut '@str.pos "a" "b" true' 5 | @@assert.noErrOut '@str.pos "a" "b" false' 6 | 7 | @@assert.equal '0' "$(@str.pos "abc" "a")" 8 | @@assert.equal '1' "$(@str.pos "abc" "b")" 9 | @@assert.equal '2' "$(@str.pos "abc" "c")" 10 | @@assert.empty "$(@str.pos "abc" "B")" 11 | @@assert.empty "$(@str.pos "abc" "B" true)" 12 | @@assert.equal '1' "$(@str.pos "abc" "b" false)" 13 | @@assert.equal '1' "$(@str.pos "abc" "B" false)" 14 | 15 | @@assert.exec '@str.pos' true '"abc" "a"' 16 | @@assert.exec '@str.pos' true '"abc" "b"' 17 | @@assert.exec '@str.pos' true '"abc" "c"' 18 | @@assert.exec '@str.pos' false '"abc" "B"' 19 | @@assert.exec '@str.pos' true '"abc" "b" true' 20 | @@assert.exec '@str.pos' false '"abc" "B" true' 21 | @@assert.exec '@str.pos' true '"abc" "b" false' 22 | @@assert.exec '@str.pos' true '"abc" "B" false' 23 | 24 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 25 | -------------------------------------------------------------------------------- /src/tests/str.repeat.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.repeat' 2 | @@assert.noErrOut '@str.repeat 0' 3 | @@assert.noErrOut '@str.repeat 1' 4 | @@assert.noErrOut '@str.repeat 1 "a"' 5 | 6 | @@assert.equal 'xxxxx' "$(@str.repeat 5 x)" 7 | @@assert.equal 'ababab' "$(@str.repeat 3 "ab")" 8 | @@assert.equal 'ab' "$(@str.repeat 1 "ab")" 9 | @@assert.empty "$(@str.repeat 0 "x")" 10 | @@assert.empty "$(@str.repeat 10)" 11 | @@assert.empty "$(@str.repeat 10 "")" 12 | 13 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 14 | -------------------------------------------------------------------------------- /src/tests/str.replace.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.replace' 2 | @@assert.noErrOut '@str.replace "a"' 3 | @@assert.noErrOut '@str.replace "a" "b"' 4 | @@assert.noErrOut '@str.replace "a" "b" "c"' 5 | @@assert.noErrOut '@str.replace "a" "b" "c"' 6 | @@assert.noErrOut '@str.replace "a" "b" "c" true' 7 | @@assert.noErrOut '@str.replace "a" "b" "c" false' 8 | 9 | @@assert.equal 'xbc' "$(@str.replace "abc" "a" "x")" 10 | @@assert.equal 'axc' "$(@str.replace "abc" "b" "x")" 11 | @@assert.equal 'abx' "$(@str.replace "abc" "c" "x")" 12 | 13 | @@assert.equal 'axc' "$(@str.replace "abc" "b" "x" true)" 14 | @@assert.equal 'axc' "$(@str.replace "abc" "b" "x" false)" 15 | @@assert.equal 'axc' "$(@str.replace "abc" "B" "x" true)" 16 | @@assert.equal 'abc' "$(@str.replace "abc" "B" "x" false)" 17 | 18 | @@assert.equal 'xbx' "$(@str.replace "abc" "[ac]" "x")" 19 | @@assert.equal 'xbx' "$(@str.replace "abc" "[AC]" "x")" 20 | @@assert.equal 'xbx' "$(@str.replace "abc" "[AC]" "x" true)" 21 | @@assert.equal 'xbx' "$(@str.replace "abc" "[ac]" "x" false)" 22 | @@assert.equal 'abc' "$(@str.replace "abc" "[AC]" "x" false)" 23 | 24 | @@assert.empty "$(@str.replace "abc" "abc" "")" 25 | @@assert.empty "$(@str.replace "" "a" "b")" 26 | @@assert.empty "$(@str.replace "abc" ".*" "")" 27 | 28 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 29 | -------------------------------------------------------------------------------- /src/tests/str.rtrim.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.rtrim' 2 | @@assert.noErrOut '@str.rtrim a' 3 | 4 | @@assert.equal ' a b1' "$(@str.rtrim ' a b1 ')" 5 | @@assert.equal ' a b2' "$(@str.rtrim ' a b2 ')" 6 | 7 | @@assert.equal 'xxx a x b4 ' "$(@str.rtrim 'xxx a x b4 xxx' 'x')" 8 | @@assert.equal 'XXX a x b5 XXX' "$(@str.rtrim 'XXX a x b5 XXX' 'x')" 9 | 10 | @@assert.equal 'ab ab ' "$(@str.rtrim 'ab ab ab' 'ab')" 11 | @@assert.equal 'ab' "$(@str.rtrim 'ab ab ab' ' ab')" 12 | @@assert.equal 'abba' "$(@str.rtrim 'abbaab' 'ab')" 13 | 14 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 15 | -------------------------------------------------------------------------------- /src/tests/str.sub.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.sub' 2 | @@assert.noErrOut '@str.sub "a"' 3 | 4 | @@assert.equal 'abc' "$(@str.sub "abcdefg" 3)" 5 | @@assert.equal 'abc' "$(@str.sub "abcdefg" 3 0)" 6 | @@assert.equal 'cde' "$(@str.sub "abcdefg" 3 2)" 7 | @@assert.equal 'fg' "$(@str.sub "abcdefg" 3 5)" 8 | @@assert.equal 'g' "$(@str.sub "abcdefg" 3 6)" 9 | 10 | @@assert.empty "$(@str.sub "")" 11 | @@assert.empty "$(@str.sub "abcdefg" 3 7)" 12 | @@assert.empty "$(@str.sub "abcdefg" 3 10)" 13 | @@assert.empty "$(@str.sub "abcdefg" 0 1)" 14 | @@assert.empty "$(@str.sub "abcdefg" 0 0)" 15 | 16 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 17 | -------------------------------------------------------------------------------- /src/tests/str.toLower.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.toLower' 2 | @@assert.noErrOut '@str.toLower "a"' 3 | 4 | @@assert.equal 'abc 123 x' "$(@str.toLower "abc 123 X")" 5 | @@assert.equal 'abc 123 x' "$(@str.toLower "Abc 123 X")" 6 | @@assert.equal 'abc 123 x' "$(@str.toLower "aBC 123 x")" 7 | 8 | @@assert.empty "$(@str.toLower "")" 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/tests/str.toUpper.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.toUpper' 2 | @@assert.noErrOut '@str.toUpper "a"' 3 | 4 | @@assert.equal 'ABC 123 X' "$(@str.toUpper "abc 123 X")" 5 | @@assert.equal 'ABC 123 X' "$(@str.toUpper "Abc 123 X")" 6 | @@assert.equal 'ABC 123 X' "$(@str.toUpper "aBC 123 x")" 7 | 8 | @@assert.empty "$(@str.toUpper "")" 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/tests/str.trim.xsh: -------------------------------------------------------------------------------- 1 | @@assert.noErrOut '@str.trim' 2 | @@assert.noErrOut '@str.trim a' 3 | 4 | @@assert.equal 'a b1' "$(@str.trim ' a b1 ')" 5 | @@assert.equal 'a b2' "$(@str.trim ' a b2 ')" 6 | 7 | @@assert.equal ' a x b4 ' "$(@str.trim 'xxx a x b4 xxx' 'x')" 8 | @@assert.equal 'XXX a x b5 XXX' "$(@str.trim 'XXX a x b5 XXX' 'x')" 9 | 10 | @@assert.equal ' ab ' "$(@str.trim 'ab ab ab' 'ab')" 11 | @@assert.equal 'ab' "$(@str.trim 'ab ab ab' 'ab ')" 12 | @@assert.equal 'ba' "$(@str.trim 'abbaab' 'ab')" 13 | 14 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 15 | -------------------------------------------------------------------------------- /src/tests/temp.path.xsh: -------------------------------------------------------------------------------- 1 | @@assert.notEmpty "${BASHX_APP_TMP_PATH}" 2 | 3 | [ -d "${BASHX_APP_TMP_PATH}" ] || @@assert.fail "Path not found: ${BASHX_APP_TMP_PATH}" 4 | 5 | touch "${BASHX_APP_TMP_PATH}/test" || @app.error 6 | @@assert.equal $? 0 7 | 8 | @log "BASHX_APP_TMP_PATH: ${BASHX_APP_TMP_PATH}" 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/tests/user.timeout.xsh: -------------------------------------------------------------------------------- 1 | if $BX_OS_IS_MAC; then 2 | @@assert.warn "This test is not working on Mac OS" 3 | return 0 4 | fi 5 | 6 | tmp_file="$(@mktemp false)" 7 | 8 | cmd_touch="touch ${tmp_file}" 9 | 10 | # Wait 1 second 11 | @log.cmd "@user.timeout 1 '${cmd_touch}'" 3>/dev/null 12 | [ ! -f "${tmp_file}" ] && @@assert.fail "${cmd_touch} has not been created!" 13 | rm -f ${tmp_file} || @@assert.fail "Error deleting ${tmp_file}!" 14 | 15 | # User input lower 'c' 16 | @log.cmd "echo 'c' | @user.timeout 2 '${cmd_touch} ; echo NO PRINT THIS'" 3>/dev/null 17 | [ -f "${tmp_file}" ] && @@assert.fail "${cmd_touch} should not have been created!" 18 | 19 | # User input uppser 'C' 20 | @log.cmd "echo 'C' | @user.timeout 2 '${cmd_touch} ; echo NO PRINT THIS'" 3>/dev/null 21 | [ -f "${tmp_file}" ] && @@assert.fail "${cmd_touch} should not have been created!" 22 | 23 | # User input ESC 24 | @log.cmd "echo -e \"\${BX_KEY_ESC}\" | @user.timeout 2 '${cmd_touch} ; echo NO PRINT THIS'" 3>/dev/null 25 | [ -f "${tmp_file}" ] && @@assert.fail "${cmd_touch} should not have been created!" 26 | 27 | # All ok 28 | return 0 29 | 30 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 31 | -------------------------------------------------------------------------------- /src/utils/README.md: -------------------------------------------------------------------------------- 1 | # BashX Utils 2 | 3 | Available _BashX_ **utils**. 4 | 5 | Each file with `xsh` extension at this path, 6 | will be a _BashX_ function prefixed with an `@`. 7 | You can navigate the tree with `.`. 8 | 9 | > **Example** 10 | > 11 | > - if you have `utils/foo.xsh`, you can use 12 | > `@foo` in your scripts. 13 | > - if you have `utils/foo/bar.xsh`, you can use 14 | > `@foo.bar` in your scripts. 15 | -------------------------------------------------------------------------------- /src/utils/app/backtrace.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Get backtrace. 3 | ## 4 | ## Out: {String} Backtrace. 5 | 6 | local i=0 7 | 8 | while caller $i ; do ((i++)) ; done 9 | 10 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 11 | -------------------------------------------------------------------------------- /src/utils/app/error.xsh: -------------------------------------------------------------------------------- 1 | ## msg [print_backtrace [code]] 2 | ## Show error message at stderr and exit. 3 | ## 4 | ## Params: 5 | ## msg: {String} Error message. 6 | ## print_backtrace: {Boolean} Show Backtrace? 7 | ## Optional. Default: false. 8 | ## code: {Integer} Exit code. 9 | ## Otional. Default: 1. 10 | 11 | local msg="$1" 12 | local print_backtrace=${2:-false} 13 | local code=${3:-1} 14 | 15 | if [ ! -z "${msg}" ]; then 16 | @log.warn 17 | @log.alert "${msg}" 18 | @log.warn 19 | fi 20 | 21 | $print_backtrace && @log.warn "Backtrace:\n$(@app.backtrace)" 22 | 23 | @app.exit ${code} 24 | 25 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 26 | -------------------------------------------------------------------------------- /src/utils/app/exit.xsh: -------------------------------------------------------------------------------- 1 | ## code 2 | ## Exit from APP and execute the function setted in "@on.exit". 3 | ## 4 | ## Params: 5 | ## code: {Integer} Exit code. 6 | ## Optional. Default: 0. 7 | ## 8 | ## Warning! 9 | ## Avoid calling this function from sub-shell. 10 | 11 | local code=${1:-0} 12 | 13 | if ! @isNumber ${code}; then 14 | code=1 15 | fi 16 | 17 | if [[ ${BX_PROC_INDEX_MAIN} != ${BASH_SUBSHELL} ]]; then 18 | # Save current process exit code 19 | echo ${code} > "${BASHX_APP_TMP_PATH}/__bx_subshell_exit" 20 | elif ! ${BX_APP_EXIT} ; then 21 | # Mark as exit 22 | BX_APP_EXIT=true 23 | 24 | # On Error 25 | if [[ ${code} -ne 0 ]] && [ -f "${BASHX_EVENTS_PATH}/error.${BX_SCRIPT_EXTENSION}" ]; then 26 | . "${BASHX_EVENTS_PATH}/error.${BX_SCRIPT_EXTENSION}" 27 | fi 28 | 29 | # Execute exit actions 30 | [ -z "${BX_ON_EXIT}" ] || ( EXIT_CODE=${code} eval "${BX_ON_EXIT}" ) 31 | 32 | # On Finish 33 | if [ -f "${BASHX_EVENTS_PATH}/finish.${BX_SCRIPT_EXTENSION}" ]; then 34 | . "${BASHX_EVENTS_PATH}/finish.${BX_SCRIPT_EXTENSION}" 35 | fi 36 | 37 | # Cleanup 38 | if [ ! -z "${BASHX_APP_TMP_PATH}" ] && [ -d "${BASHX_APP_TMP_PATH}" ]; then 39 | rm -rf "${BASHX_APP_TMP_PATH}" || true 40 | fi 41 | 42 | # Reset System color 43 | @style reset >&3 44 | 45 | # Space 46 | echo >&3 47 | fi 48 | 49 | # Exit 50 | exit ${code} 51 | 52 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 53 | -------------------------------------------------------------------------------- /src/utils/app/info.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Print APP title and version. 3 | ## 4 | ## Out: {String} APP title and version. 5 | 6 | echo "${BASHX_APP_TITLE} v${BASHX_APP_VERSION}" 7 | 8 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 9 | -------------------------------------------------------------------------------- /src/utils/app/run.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Run APP. 3 | ## 4 | ## Use: At end of /app file, put next: 5 | ## @app.run 6 | 7 | # __BX_style_reset="$(${BASHX_APP_COLORS_ENABLED} && @style reset || true)" 8 | 9 | # __BX_on_stdout() { 10 | # local line 11 | # while read line; do 12 | # [ -z "${BX_ON_STDOUT}" ] || line="$(OUT_LINE="${line}" eval "${BX_ON_STDOUT}")" 13 | # echo -e "${__BX_style_reset}${line}" 14 | # done 15 | # } 16 | 17 | # __BX_on_stderr() { 18 | # local line 19 | # while read line; do 20 | # [ -z "${BX_ON_STDERR}" ] || line="$(OUT_LINE="${line}" eval "${BX_ON_STDERR}")" 21 | # echo -e "${line}" >&2 22 | # done 23 | # } 24 | 25 | # __BX_on_stdinfo() { 26 | # local line 27 | # while read line; do 28 | # [ -z "${BX_ON_STDINFO}" ] || line="$(OUT_LINE="${line}" eval "${BX_ON_STDINFO}")" 29 | # echo -e "${__BX_style_reset}${line}" >&3 30 | # done 31 | # } 32 | 33 | ${BASHX_APP_COLORS_ENABLED} && local err_char='❌❌❌' || local err_char='!!!' 34 | 35 | { 36 | # Call @app.run once 37 | unset -f @app.run 38 | 39 | # Start 40 | local r=1 41 | 42 | echo >&3 # Space 43 | 44 | # Get first argument as action 45 | export BX_ACTION="${BX_SCRIPT_ARGS[0]}" 46 | 47 | @log.title "$(@app.info)" 48 | 49 | # On end Script from Error 50 | trap '@app.error "${err_char} Unexpected error [$?]" true $?' ERR 51 | 52 | # On end Script 53 | # https://mywiki.wooledge.org/SignalTrap 54 | # https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html 55 | trap '@app.exit $?' HUP INT QUIT KILL TERM EXIT 56 | 57 | # Execution Watcher 58 | # https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html 59 | trap '{ 60 | __BX_each_line $? 61 | } 2>/dev/null' DEBUG 62 | 63 | if [ -z "${BX_ACTION}" ]; then 64 | # Empty Action 65 | 66 | if [ -z "${BASHX_APP_DEFAULT_ACTION}" ]; then 67 | # Show help 68 | BX_ACTION='help' 69 | r=1 70 | else 71 | # Call default action 72 | BX_ACTION="${BASHX_APP_DEFAULT_ACTION}" 73 | fi 74 | 75 | export BX_ACTION="${BX_ACTION}" 76 | ${BX_ACTION_PREFIX}.${BX_ACTION} 77 | elif [[ ${#BX_SCRIPT_ARGS[@]} -gt 0 ]]; then 78 | # Remove "Action" from parameters 79 | readonly BX_SCRIPT_ARGS=(${BX_SCRIPT_ARGS[@]:1}) 80 | readonly BX_ACTION="${BX_ACTION}" 81 | 82 | # On Ready 83 | if [ -f "${BASHX_EVENTS_PATH}/ready.${BX_SCRIPT_EXTENSION}" ]; then 84 | . "${BASHX_EVENTS_PATH}/ready.${BX_SCRIPT_EXTENSION}" 85 | fi 86 | 87 | # If function (Action) exists 88 | if @function.exists "${BX_ACTION_PREFIX}.${BX_ACTION}" ; then 89 | # On Start 90 | if [ -f "${BASHX_EVENTS_PATH}/start.${BX_SCRIPT_EXTENSION}" ]; then 91 | . "${BASHX_EVENTS_PATH}/start.${BX_SCRIPT_EXTENSION}" 92 | fi 93 | 94 | # Exec 95 | ${BX_ACTION_PREFIX}.${BX_ACTION} "${BX_SCRIPT_ARGS[@]}" 96 | r=$? 97 | else 98 | # Invalid Action 99 | if [ -f "${BASHX_EVENTS_PATH}/invalid-action.${BX_SCRIPT_EXTENSION}" ]; then 100 | . "${BASHX_EVENTS_PATH}/invalid-action.${BX_SCRIPT_EXTENSION}" 101 | else 102 | local _sr="$(@style default color:red)" 103 | @app.error "Action '$(@style color:green)${BX_ACTION}${_sr}' not found. See '$(@style bold)help${_sr}' for help." 104 | fi 105 | fi 106 | fi 107 | 108 | # Return result code 109 | @app.exit $r 110 | } #> >(__BX_on_stdout) 2> >(__BX_on_stderr) 3> >(__BX_on_stdinfo) 111 | 112 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 113 | -------------------------------------------------------------------------------- /src/utils/args.xsh: -------------------------------------------------------------------------------- 1 | ## options* 2 | ## Read options. 3 | ## Note: Verbose output if you use `set -x` 4 | ## 5 | ## Params: 6 | ## options*: {Map} Valid options specifications. 7 | ## 8 | ## Variables: 9 | ## BASHX_args_fail_on_unexpected: {Boolean} Fail on unexpected argument? 10 | ## Default: true 11 | ## 12 | ## 13 | ## Options Format: [VARIABLE:KEY:INPUT]* 14 | ## where: 15 | ## VARIABLE: {String} Valid local variable name (without spaces). 16 | ## This produces a local variable with this name. 17 | ## KEY: {Char} Full option key starting with "-". 18 | ## Use `|` to concatenate multiple options. 19 | ## INPUT: {Boolean} Option with input? 20 | ## Optional. Default: false. 21 | ## 22 | ## Options Example: 'new:-n' 'path:-p:true' 'foo:-x:false' 23 | ## This produces next variables: 24 | ## - "new" with TRUE when "-n" is present or FALSE when "-n" is not present. 25 | ## - "path" with user inputs values as Array, or EMPTY on empty user input. 26 | ## - "foo" with TRUE when "-x" is present or FALSE when "-x" is not present. 27 | ## 28 | ## Usage example: 29 | ## eval "$(@args 'new:-n|-N' 'path:-p|--path:true')" 30 | ## @log "'-n|-N' parameter: ${args_new}" 31 | ## @log "'-p|--path' parameter: ${args_path[@]} (${#args_path[@]})" 32 | 33 | { 34 | local _d_options_debug="\$-" 35 | set +x 36 | } 2>/dev/null 37 | 38 | local v 39 | local options="$@" 40 | local variable 41 | local config 42 | local config_key 43 | local config_var 44 | local config_var_val_def 45 | local config_var_val 46 | local config_input 47 | local script_vars='' 48 | local script_case='' 49 | local OIFS="$IFS" 50 | local variables 51 | 52 | IFS=' ' variables=(${options}) IFS="$OIFS" 53 | 54 | for variable in ${variables[@]} ; do 55 | IFS=':' config=(${variable}) IFS="$OIFS" 56 | config_var="${config[0]}" 57 | config_key="${config[1]}" 58 | config_input=${config[2]:-false} 59 | 60 | [[ ${#config_key} -ge 2 ]] || @throw.invalidParam config_key 'Invalid KEY. Showld contains 2 or more characters' 61 | [[ "${config_key}" == -* ]] || @throw.invalidParam config_key 'Invalid KEY. Should start with "-"' 62 | [ ! -z "${config_var}" ] || @throw.invalidParam config_var 'Empty VARIABLE' 63 | @isBoolean "${config_input}" || @throw.invalidParam config_input 'Invalid INPUT value' 64 | 65 | if ${config_input}; then 66 | config_var_val_def='=()' 67 | config_var_val='+=($1) ; shift' 68 | else 69 | config_var_val_def='=false' 70 | config_var_val='=true' 71 | fi 72 | 73 | v="args_${config_var}" 74 | # See @code.variableClean 75 | v="${v//[^a-zA-Z0-9]/_}" 76 | 77 | [ -z "${script_vars}" ] || script_vars="${script_vars}${BX_CHAR_NL}" 78 | [ -z "${script_case}" ] || script_case="${script_case}${BX_CHAR_NL}${BX_CHAR_TAB}${BX_CHAR_TAB}" 79 | script_vars="${script_vars}local ${v}${config_var_val_def}" 80 | script_case="${script_case}${config_key}) shift ; ${v}${config_var_val} ;;" 81 | done 82 | 83 | cat </dev/null 2>&1 109 | 110 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 111 | -------------------------------------------------------------------------------- /src/utils/array/contains.xsh: -------------------------------------------------------------------------------- 1 | ## source search_array 2 | ## Array contains an element? 3 | ## 4 | ## Params: 5 | ## source: {String} Element/String to search. 6 | ## search_array: {Array} Array where search for. 7 | ## 8 | ## Return: 0 if contains the search string, 1 if not contains the search string. 9 | ## 10 | ## Usage example: 11 | ## @array.contains "abc" "${array[@]}" 12 | 13 | [ -z "$2" ] && return 1 14 | 15 | local e 16 | for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done 17 | return 1 18 | 19 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 20 | -------------------------------------------------------------------------------- /src/utils/asserts/load.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Load asserts functions. 3 | ## 4 | 5 | # Single load 6 | [ ! -z "${__ASSERTION_LOADED__}" ] && return 0 7 | __ASSERTION_LOADED__=true 8 | 9 | ASSERTION_EXEC_OUT_STD="$(@mktemp false)" 10 | ASSERTION_EXEC_OUT_ERR="$(@mktemp false)" 11 | ASSERTION_EXEC_OUT_INF="$(@mktemp false)" 12 | 13 | @@assert._assertFail() { # INTERNAL 14 | @log.alert "Assert Fail: $@" 15 | exit 1 16 | } 17 | 18 | @@assert._testAssertFail() { # INTERNAL 19 | @log " - Test: [$1]" 20 | @log " - Exit code: $2" 21 | @log " - Expected result: $3" 22 | @log " - STD OUT: [$([ -f "${ASSERTION_EXEC_OUT_STD}" ] && cat "${ASSERTION_EXEC_OUT_STD}")]" 23 | @log " - ERR OUT: [$([ -f "${ASSERTION_EXEC_OUT_ERR}" ] && cat "${ASSERTION_EXEC_OUT_ERR}")]" 24 | @log " - INFO OUT: [$([ -f "${ASSERTION_EXEC_OUT_INF}" ] && cat "${ASSERTION_EXEC_OUT_INF}")]" 25 | @@assert._assertFail "$4" 26 | } 27 | 28 | ## @@assert.fail message 29 | ## Test fail. 30 | ## Params: 31 | ## message: Message about the fail. 32 | @@assert.fail() { # message 33 | @@assert._assertFail "${FUNCNAME[0]} [$@] ($(caller))" 34 | } 35 | 36 | ## @@assert.warn message 37 | ## Test warning. 38 | ## Params: 39 | ## message: Message about the warning. 40 | @@assert.warn() { # message 41 | @log.warn "${FUNCNAME[0]} [$@] ($(caller))" 42 | } 43 | 44 | ## @@assert.true var 45 | ## Test if {var} is boolean true. 46 | ## Params: 47 | ## var: Variable to test. 48 | @@assert.true() { # var 49 | if [[ "$1" != true ]]; then 50 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 51 | fi 52 | return 0 53 | } 54 | 55 | ## @@assert.false var 56 | ## Test if {var} is boolean false. 57 | ## Params: 58 | ## var: Variable to test. 59 | @@assert.false() { # var 60 | if [[ "$1" != false ]]; then 61 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 62 | fi 63 | return 0 64 | } 65 | 66 | ## @@assert.empty var 67 | ## Test if {var} is empty. 68 | ## Params: 69 | ## var: Variable to test. 70 | @@assert.empty() { # var 71 | if [[ ! -z "$1" ]]; then 72 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 73 | fi 74 | return 0 75 | } 76 | 77 | ## @@assert.notEmpty var 78 | ## Test if {var} is not empty. 79 | ## Params: 80 | ## var: Variable to test. 81 | @@assert.notEmpty() { # var 82 | if [[ -z "$1" ]]; then 83 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 84 | fi 85 | return 0 86 | } 87 | 88 | ## @@assert.number var 89 | ## Test if {var} is a number. 90 | ## Params: 91 | ## var: Variable to test. 92 | @@assert.number() { # var 93 | if \ 94 | [[ -z "$1" ]] || \ 95 | ! [[ "$1" =~ ^[0-9][0-9]*$ ]] 96 | then 97 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 98 | fi 99 | return 0 100 | } 101 | 102 | ## @@assert.errorCode code 103 | ## Test if exit code is an error code. 104 | ## Params: 105 | ## code: Variable to test. 106 | @@assert.errorCode() { # code 107 | @@assert.number "$1" 108 | if \ 109 | [[ "$1" -eq 0 ]] || \ 110 | [[ "$1" -gt 255 ]] 111 | then 112 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 113 | fi 114 | return 0 115 | } 116 | 117 | ## @@assert.notErrorCode code 118 | ## Test if exit code is not an error code. 119 | ## Params: 120 | ## code: Variable to test. 121 | @@assert.notErrorCode() { # code 122 | @@assert.number "$1" 123 | if [[ "$1" -gt 0 ]]; then 124 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 125 | fi 126 | return 0 127 | } 128 | 129 | ## @@assert.equal str_a str_b 130 | ## Test if {str_a} is equal to {str_b}. 131 | ## Params: 132 | ## str_a: Expected value. 133 | ## str_b: Variable to test. 134 | @@assert.equal() { # str_a str_b 135 | if [[ "$1" != "$2" ]]; then 136 | @@assert._assertFail "${FUNCNAME[0]} [$1] [$2] ($(caller))" 137 | fi 138 | return 0 139 | } 140 | 141 | ## @@assert.notEqual str_a str_b 142 | ## Test if {str_a} is different to {str_b}. 143 | ## Params: 144 | ## str_a: Unexpected value. 145 | ## str_b: Variable to test. 146 | @@assert.notEqual() { # str_a str_b 147 | if [[ "$1" == "$2" ]]; then 148 | @@assert._assertFail "${FUNCNAME[0]} [$1] [$2] ($(caller))" 149 | fi 150 | return 0 151 | } 152 | 153 | ## @@assert.contains str_base str_search 154 | ## Test if {str_base} contains {str_search}. 155 | ## Params: 156 | ## str_base: String where search. 157 | ## str_search: String to search. 158 | @@assert.contains() { # str_base str_search 159 | if \ 160 | [[ -z "$1" ]] || \ 161 | [[ -z "$2" ]] || \ 162 | [[ "$1" != *"$2"* ]] 163 | then 164 | @@assert._assertFail "${FUNCNAME[0]} [$1] [$2] ($(caller))" 165 | fi 166 | return 0 167 | } 168 | 169 | ## @@assert.notContains str_base str_search 170 | ## Test if {str_base} doesn't contains {str_search}. 171 | ## Params: 172 | ## str_base: String where search. 173 | ## str_search: String to search. 174 | @@assert.notContains() { # str_base str_search 175 | if \ 176 | [[ -z "$1" ]] || \ 177 | [[ -z "$2" ]] || \ 178 | [[ "$1" == *"$2"* ]] 179 | then 180 | @@assert._assertFail "${FUNCNAME[0]} [$1] [$2] ($(caller))" 181 | fi 182 | return 0 183 | } 184 | 185 | ## @@assert.startWith str_base str_search_start 186 | ## Test if {str_base} starts with {str_search_start}. 187 | ## Params: 188 | ## str_base: String where search. 189 | ## str_search_start: String to search. 190 | @@assert.startWith() { # str_base str_search_start 191 | if \ 192 | [[ -z "$1" ]] || \ 193 | [[ -z "$2" ]] || \ 194 | [[ "$1" != "$2"* ]] 195 | then 196 | @@assert._assertFail "${FUNCNAME[0]} [$1] [$2] ($(caller))" 197 | fi 198 | return 0 199 | } 200 | 201 | ## @@assert.endWith str_base str_search_end 202 | ## Test if {str_base} ends with {str_search_end}. 203 | ## Params: 204 | ## str_base: String where search. 205 | ## str_search_end: String to search. 206 | @@assert.endWith() { # str_base str_search_end 207 | if \ 208 | [[ -z "$1" ]] || \ 209 | [[ -z "$2" ]] || \ 210 | [[ "$1" != *"$2" ]] 211 | then 212 | @@assert._assertFail "${FUNCNAME[0]} [$1] [$2] ($(caller))" 213 | fi 214 | return 0 215 | } 216 | 217 | ## @@assert.stdOut cmd 218 | ## Test if {cmd} have standard output. 219 | ## Params: 220 | ## cmd: Command to execute. 221 | @@assert.stdOut() { # cmd 222 | local result="$( eval "$1" 2>/dev/null 3>/dev/null )" 223 | if [[ -z "${result}" ]]; then 224 | @@assert._assertFail "${FUNCNAME[0]} [$1] Output: [${result}] ($(caller))" 225 | fi 226 | return 0 227 | } 228 | 229 | ## @@assert.noStdOut cmd 230 | ## Test if {cmd} doesn't have standard output. 231 | ## Params: 232 | ## cmd: Command to execute. 233 | @@assert.noStdOut() { # cmd 234 | local result="$( eval "$1" 2>/dev/null 3>/dev/null )" 235 | if [[ ! -z "${result}" ]]; then 236 | @@assert._assertFail "${FUNCNAME[0]} [$1] Output: [${result}] ($(caller))" 237 | fi 238 | return 0 239 | } 240 | 241 | ## @@assert.errOut cmd 242 | ## Test if {cmd} have error output. 243 | ## Params: 244 | ## cmd: Command to execute. 245 | @@assert.errOut() { # cmd 246 | local result="$( ( eval "$1" >/dev/null 3>/dev/null ) 2>&1 )" 247 | if [[ -z "${result}" ]]; then 248 | @@assert._assertFail "${FUNCNAME[0]} [$1] Output: [${result}] ($(caller))" 249 | fi 250 | return 0 251 | } 252 | 253 | ## @@assert.noErrOut cmd 254 | ## Test if {cmd} doesn't have error output. 255 | ## Params: 256 | ## cmd: Command to execute. 257 | @@assert.noErrOut() { # cmd 258 | local result="$( ( eval "$1" >/dev/null 3>/dev/null ) 2>&1 )" 259 | if [[ ! -z "${result}" ]]; then 260 | @@assert._assertFail "${FUNCNAME[0]} [$1] Output: [${result}] ($(caller))" 261 | fi 262 | return 0 263 | } 264 | 265 | ## @@assert.noOut cmd 266 | ## Test if {cmd} doesn't have output. 267 | ## Params: 268 | ## cmd: Command to execute. 269 | @@assert.noOut() { # cmd 270 | local result="$( eval "$1" 2>&1 3>&1 )" 271 | if [[ ! -z "${result}" ]]; then 272 | @@assert._assertFail "${FUNCNAME[0]} [$1] Output: [${result}] ($(caller))" 273 | fi 274 | return 0 275 | } 276 | 277 | ## @@assert.exec fn_name expected_exit [params] 278 | ## Test function. 279 | ## Params: 280 | ## fn_name: Function name to execute. 281 | ## expected_exit: {Integer} Expected exit code. 282 | ## params: Function parameters. 283 | ## Optional. 284 | @@assert.exec() { # fn_name expected_exit [params] 285 | local fn_name="$1" 286 | local expected_exit=$2 287 | local params="$3" 288 | 289 | [ -f "${ASSERTION_EXEC_OUT_STD}" ] && rm -f "${ASSERTION_EXEC_OUT_STD}" 290 | [ -f "${ASSERTION_EXEC_OUT_ERR}" ] && rm -f "${ASSERTION_EXEC_OUT_ERR}" 291 | [ -f "${ASSERTION_EXEC_OUT_INF}" ] && rm -f "${ASSERTION_EXEC_OUT_INF}" 292 | 293 | local cmd="${fn_name} ${params}" 294 | ( 295 | eval "${cmd}" \ 296 | >"${ASSERTION_EXEC_OUT_STD}" \ 297 | 2>"${ASSERTION_EXEC_OUT_ERR}" \ 298 | 3>"${ASSERTION_EXEC_OUT_INF}" 299 | exit $? 300 | ) 301 | local result=$? 302 | 303 | if [[ "${expected_exit}" =~ ^[0-9][0-9]*$ ]]; then 304 | [[ ${result} -eq ${expected_exit} ]] \ 305 | && @@assert._testAssertFail "${cmd}" ${result} ${expected_exit} "$(caller)" 306 | elif ${expected_exit}; then 307 | [[ ${result} -ne 0 ]] \ 308 | && @@assert._testAssertFail "${cmd}" ${result} ${expected_exit} "$(caller)" 309 | else 310 | [[ ${result} -eq 0 ]] \ 311 | && @@assert._testAssertFail "${cmd}" ${result} ${expected_exit} "$(caller)" 312 | fi 313 | 314 | return 0 315 | } 316 | 317 | ## @@assert.regExp reg_exp str 318 | ## Test if {str} match with {reg_exp}. 319 | ## Params: 320 | ## reg_exp: RegExp to match. 321 | ## str: String to check. 322 | @@assert.regExp() { # reg_exp str 323 | if \ 324 | [[ -z "$1" ]] || \ 325 | [[ -z "$2" ]] || \ 326 | ! [[ "$2" =~ $1 ]] 327 | then 328 | @@assert._assertFail "${FUNCNAME[0]} [$1] ($(caller))" 329 | fi 330 | return 0 331 | } 332 | 333 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 334 | -------------------------------------------------------------------------------- /src/utils/checkError.xsh: -------------------------------------------------------------------------------- 1 | ## code [cmd] 2 | ## Check error. 3 | ## 4 | ## Params: 5 | ## code: {Integer} Exit code. Example: $? 6 | ## cmd: {String} Command to execute on error. 7 | ## Optional. 8 | ## 9 | ## Example: @checkError $? "error 'Invalid operation'". 10 | 11 | local code=$1 12 | local cmd="$2" 13 | 14 | if @isNumber "${code}"; then 15 | if [[ ${code} -gt 0 ]]; then 16 | # Error 17 | eval "${cmd}" 18 | fi 19 | else 20 | # Error 21 | eval "${cmd}" 22 | fi 23 | 24 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 25 | -------------------------------------------------------------------------------- /src/utils/checkErrorEnd.xsh: -------------------------------------------------------------------------------- 1 | ## code [cmd] 2 | ## Check error and finish if `code` is bigger than 0 (error). 3 | ## 4 | ## Params: 5 | ## code: {Integer} Exit code. Example: $? 6 | ## cmd: {String} Command to execute on error. 7 | ## Optional. 8 | ## 9 | ## Example: @checkError $? "Invalid operation". 10 | 11 | local code=$1 12 | local cmd="$2" 13 | 14 | if @isNumber "${code}"; then 15 | if [[ ${code} -gt 0 ]]; then 16 | # Error 17 | [ ! -z "${cmd}" ] && eval "${cmd}" 18 | @app.error '' true ${code} 19 | fi 20 | else 21 | # Error 22 | [ ! -z "${cmd}" ] && eval "${cmd}" 23 | @app.error '' true ${code} 24 | fi 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/code/scriptMinify.xsh: -------------------------------------------------------------------------------- 1 | ## << 2 | ## Script minify. 3 | ## 4 | ## Input (read from /dev/stdin): Script content. 5 | ## Out: {String} Minified script. 6 | 7 | # Result 8 | local minify='' 9 | 10 | # Current line 11 | local line 12 | 13 | while read line; do 14 | line="$(@str.trim "${line}")" 15 | minify="$(@str.trim "${minify}")" 16 | 17 | # If it's a non empty line... 18 | if \ 19 | [[ ! -z "${line}" ]] && \ 20 | [[ "${line}" != '#'* ]] 21 | then 22 | if [[ ! -z "${minify}" ]] && \ 23 | [[ "${line}" != ')' ]] 24 | then 25 | # If end with... 26 | if [[ "${minify}" == *'{' ]] || \ 27 | [[ "${minify}" == *'[' ]] || \ 28 | [[ "${minify}" == *';do' ]] || \ 29 | [[ "${minify}" == *' do' ]] || \ 30 | [[ "${minify}" == *';then' ]] || \ 31 | [[ "${minify}" == *' then' ]] || \ 32 | [[ "${minify}" == *';else' ]] || \ 33 | [[ "${minify}" == *' else' ]] || \ 34 | [[ "${minify}" == *'(' ]] 35 | then 36 | # Add an extra space at the end 37 | minify="${minify} " 38 | else 39 | # Add `;` at the end 40 | minify="${minify};" 41 | fi 42 | fi 43 | 44 | # Cleanup line 45 | line="$(@str.replace "${line}" '\s+;\s+' ';')" 46 | line="$(@str.replace "${line}" '\s+' ' ')" 47 | 48 | # Concat current line 49 | minify="${minify}${line}" 50 | fi 51 | done < /dev/stdin 52 | 53 | # Script output 54 | echo "${minify}" 55 | 56 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 57 | -------------------------------------------------------------------------------- /src/utils/code/variableClean.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Clean variable name. 3 | ## Replace all non valid character by "_". 4 | ## 5 | ## Params: 6 | ## *: {String} String to clean. 7 | ## 8 | ## Out: {String} Valid variable name. 9 | 10 | echo "${*//[^a-zA-Z0-9]/_}" 11 | 12 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 13 | -------------------------------------------------------------------------------- /src/utils/config/test.xsh: -------------------------------------------------------------------------------- 1 | ## config_file [fail_on_error] 2 | ## Validate configuration file. 3 | ## 4 | ## Params: 5 | ## config_file: {String} Config file path. 6 | ## fail_on_error: {Boolean} Fail on error? 7 | ## Default: true 8 | ## 9 | ## Return: 0 on valid, 1 on error. 10 | 11 | local config_file="${1}" 12 | local fail_on_error=${2:-true} 13 | 14 | @log "Validating config file '${config_file}'..." 15 | 16 | # Test config 17 | ( set -e +x 18 | . "${config_file}" 19 | ) && return 0 || { 20 | # Show config errors 21 | ( set -ex 22 | . "${config_file}" 23 | ) || true 24 | 25 | if ${fail_on_error}; then 26 | @app.error "Invalid config file! '${config_file}'" 27 | else 28 | return 1 29 | fi 30 | } 31 | 32 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 33 | -------------------------------------------------------------------------------- /src/utils/file/contains.xsh: -------------------------------------------------------------------------------- 1 | ## text file 2 | ## Check if file contains text. 3 | ## 4 | ## Params: 5 | ## text: {String} Text to search. 6 | ## file: {String} File path. 7 | ## 8 | ## Return: 0 if file contains the text, 1 if not contains the text. 9 | 10 | local text="$1" 11 | local file="$2" 12 | 13 | if [ ! -z "${file}" ] && [ -f "${file}" ] && [ ! -z "${text}" ]; then 14 | if grep -qis "$(@str.escape "${text}")" "$(@str.escape "${file}")" ; then 15 | # Contains 16 | return 0 17 | fi 18 | fi 19 | 20 | # Not contains 21 | return 1 22 | 23 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 24 | -------------------------------------------------------------------------------- /src/utils/file/ensureLineExists.xsh: -------------------------------------------------------------------------------- 1 | ## file line [sudo] 2 | ## Ensulre line exists in file. 3 | ## 4 | ## Params: 5 | ## file: {String} File path. 6 | ## line: {String} Line content. 7 | ## sudo: {Boolean} 8 | ## Optional. Default: false 9 | ## 10 | ## Return: 0 if line has been added. 11 | ## 1 if line already exists. 12 | ## 2 on error. 13 | 14 | local file="${1}" 15 | local line="${2}" 16 | local sudo=${3:-false} 17 | 18 | if grep -qxF "${line}" "${file}" 19 | then 20 | # Already exists 21 | return 1 22 | else 23 | if ${sudo}; then 24 | if @sudo "${file}" "echo >> '${file}' && echo '${line}' >> '${file}'" 25 | then 26 | # Line added 27 | return 0 28 | else 29 | # Error 30 | return 2 31 | fi 32 | else 33 | if echo >> "${file}" && echo "${line}" >> "${file}" 34 | then 35 | # Line added 36 | return 0 37 | else 38 | # Error 39 | return 2 40 | fi 41 | fi 42 | fi 43 | 44 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 45 | -------------------------------------------------------------------------------- /src/utils/file/name.xsh: -------------------------------------------------------------------------------- 1 | ## file [remove_extension] 2 | ## Get file name. 3 | ## 4 | ## Params: 5 | ## file: {String} File path. 6 | ## remove_extension: {Boolean} Remove file extension from file name. 7 | ## Optional. Default: false. 8 | ## 9 | ## Out: {String} File name. 10 | 11 | local file="$1" 12 | local remove_extension=${2:-false} 13 | 14 | if [[ ! -z "${file}" ]]; then 15 | # Remove path 16 | local _fname="$(basename "${file}")" 17 | 18 | # Remove extension 19 | ${remove_extension} && _fname="${_fname%.*}" || true 20 | 21 | # Result 22 | echo "${_fname}" 23 | fi 24 | 25 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 26 | -------------------------------------------------------------------------------- /src/utils/function/exists.xsh: -------------------------------------------------------------------------------- 1 | ## func_name 2 | ## Check if function exists. 3 | ## 4 | ## Params: 5 | ## func_name: {String} Function name. 6 | ## 7 | ## Return: 0 if function exists, 1 if file not exists. 8 | 9 | local func_name="$1" 10 | 11 | if [ ! -z "${func_name}" ]; then 12 | declare -f "${func_name}" >/dev/null 2>&1 && return 0 13 | fi 14 | 15 | return 1 16 | 17 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 18 | -------------------------------------------------------------------------------- /src/utils/function/load.xsh: -------------------------------------------------------------------------------- 1 | ## functions_path [prefix] 2 | ## Generate *.xsh functions from path. 3 | ## 4 | ## Params: 5 | ## functions_path: {String} Path to scripts. 6 | ## prefix: {String} Prefix. 7 | ## Optional. Default: `functions_path` directory name. 8 | 9 | { 10 | local _d_load_function_debug="$-" 11 | } 2>/dev/null 12 | 13 | local functions_path="$1" 14 | local prefix="${2:-$(@file.name "${functions_path}" true).}" 15 | local file_path 16 | 17 | [ ! -z "${functions_path}" ] || @throw.invalidParam functions_path 18 | [ -d "${functions_path}" ] || @throw.invalidParam functions_path 'Is not a valid path' 19 | 20 | _function_load_v() { 21 | local n="$(@file.name "${1}" true)" 22 | echo "${prefix}${n}" 23 | } 24 | 25 | for file_path in "${functions_path}"/* ; do 26 | if [[ -f "${file_path}" ]] && [[ "${file_path}" = *".${BX_SCRIPT_EXTENSION}" ]]; then 27 | # Create base util function 28 | local n="$(_function_load_v "${file_path}")" 29 | 30 | # See @code.variableClean 31 | local v="${n//[^a-zA-Z0-9]/_}" 32 | 33 | eval " 34 | ${n}() { 35 | { 36 | local _d_${v}_debug=\"\$-\" 37 | set +x 38 | 39 | # Execution Watcher 40 | # https://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html 41 | trap '{ 42 | __BX_each_line \$? 43 | } 2>/dev/null' DEBUG 44 | } 2>/dev/null 45 | 46 | local this='${n}' 47 | local this_path='${file_path}' 48 | . \"${file_path}\" 49 | local _r_${v}=\$? 50 | 51 | { 52 | [[ \"\${_d_${v}_debug}\" == *x* ]] && set -x || true 53 | } 2>/dev/null 54 | return \${_r_${v}} 55 | } 56 | " 57 | elif [ -d "${file_path}" ]; then 58 | # Load functions from sub-path 59 | @function.load "${file_path}" "$(_function_load_v "${file_path}")." 60 | fi 61 | done 62 | 63 | { 64 | [[ "${_d_load_function_debug}" == *x* ]] && set -x || true 65 | } 2>/dev/null 66 | 67 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 68 | -------------------------------------------------------------------------------- /src/utils/function/void.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## VOID function. 3 | ## Used in empty functions. 4 | ## Note: It's an alias of `:`. 5 | 6 | : 7 | 8 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 9 | -------------------------------------------------------------------------------- /src/utils/isBoolean.xsh: -------------------------------------------------------------------------------- 1 | ## var 2 | ## Check if is boolean. 3 | ## 4 | ## Params: 5 | ## var: Variable to check if is boolean. Valid values: "true" and "false". 6 | ## 7 | ## Return: 0 if variable is boolean, 1 if variable is not boolean. 8 | 9 | local var="$1" 10 | 11 | if [[ "${var}" == 'true' ]] || [[ "${var}" == 'false' ]]; then 12 | return 0 13 | fi 14 | 15 | return 1 16 | 17 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 18 | -------------------------------------------------------------------------------- /src/utils/isNumber.xsh: -------------------------------------------------------------------------------- 1 | ## var 2 | ## Check if is a number. 3 | ## 4 | ## Params: 5 | ## var: Variable to check if is a number. 6 | ## 7 | ## Return: 0 if variable is a number, 1 if variable is not a number. 8 | 9 | local var="$1" 10 | 11 | if [[ "${var}" =~ ^[0-9]+$ ]]; then 12 | return 0 13 | fi 14 | 15 | return 1 16 | 17 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 18 | -------------------------------------------------------------------------------- /src/utils/isRoot.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Check if run as Root. 3 | ## 4 | ## Return: 0 if is root, 1 is not root. 5 | 6 | if [[ "$(id -u)" -ne 0 ]]; then 7 | # No Root 8 | return 1 9 | fi 10 | 11 | # Root 12 | return 0 13 | 14 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 15 | -------------------------------------------------------------------------------- /src/utils/log.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Print at screen. 3 | ## 4 | ## Params: 5 | ## *: {String} Text to print. 6 | 7 | local str="$@" 8 | local line 9 | 10 | [ ! -z "${__print_var_prefix__}" ] || __print_var_prefix__="\r$(@style)${BASHX_APP_PRINT_PREFIX} " 11 | 12 | # \t -> \s*2 13 | str="$(@str.replace "${str}" '\t' ' ')" 14 | 15 | # Print 16 | echo -e "${str}" | while IFS= read -r line ; do 17 | echo -e "${__print_var_prefix__}${line}${BX_KEY_ESC}[0m\r" >&3 18 | done 19 | 20 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 21 | -------------------------------------------------------------------------------- /src/utils/log/alert.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Show red alert message. 3 | ## 4 | ## Params: 5 | ## *: {String} Message. 6 | 7 | @log.warn "$(@style color:red)$@" 8 | 9 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 10 | -------------------------------------------------------------------------------- /src/utils/log/cmd.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Print command and their result. 3 | ## 4 | ## Params: 5 | ## *: {String} Command to print and execute. 6 | ## 7 | ## Out: {String} Command executed and result. 8 | ## Return: Executed command exit code. 9 | 10 | local cmd="$@" 11 | 12 | @log 13 | @log.line 14 | 15 | @log "\$ ${cmd}" 16 | eval "${cmd}" 17 | local r=$? 18 | 19 | @log 20 | @log "\$ ${cmd} > Exit code: ${r}" 21 | @log.line 22 | @log 23 | 24 | return ${r} 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/log/line.xsh: -------------------------------------------------------------------------------- 1 | ## [len [str [prefix [suffix]]]] 2 | ## Print line with {str}. 3 | ## 4 | ## Params: 5 | ## len: {Integer} Line length ({str} repeat count). 6 | ## Optional. Default: ${BASHX_APP_WIDTH}. 7 | ## str: {String} String to repeat in line. 8 | ## Optional. Default: '='. 9 | ## prefix: {String} Line prefix. 10 | ## Optional. Default: ''. 11 | ## suffix: {String} Line suffix. 12 | ## Optional. Default: ''. 13 | 14 | local len=${1:-${BASHX_APP_WIDTH}} 15 | local str="${2:-=}" 16 | local prefix="${3}" 17 | local suffix="${4}" 18 | 19 | @log "${prefix}$(@str.repeat ${len} "${str}")${suffix}" 20 | 21 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 22 | -------------------------------------------------------------------------------- /src/utils/log/rewrite.xsh: -------------------------------------------------------------------------------- 1 | ## text [lines] 2 | ## Go back {lines} lines and print the text. 3 | ## 4 | ## Params: 5 | ## text: {String} Text to print. 6 | ## lines: {Integer} Lines to meve back. 7 | ## Optional. Default: 1. 8 | ## width: {Integer} Line width. 9 | ## Optional. Default: ${BASHX_APP_WIDTH}. 10 | 11 | 12 | local text="$1" 13 | local lines=${2} 14 | local width=${3:-${BASHX_APP_WIDTH}} 15 | 16 | if [[ -z "${lines}" ]]; then 17 | lines=1 18 | elif ! @isNumber "${lines}" ; then 19 | lines=1 20 | fi 21 | 22 | if [[ ${lines} -gt 0 ]]; then 23 | local bl="\r\033[${lines}A" 24 | echo -e "${bl}$(@style)${BASHX_APP_PRINT_PREFIX} ${text}$(@style system)\r" >&3 # Clear line 25 | @str.repeat ${width} ' ' >&3 26 | fi 27 | 28 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 29 | -------------------------------------------------------------------------------- /src/utils/log/time.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Write to LOG to Console. 3 | ## 4 | ## *: {String} Text to log. 5 | 6 | printf "¬ %.23s | %s:%s > %s\n" $(date +%F.%T.%N) ${BASH_SOURCE[2]##*/} ${BASH_LINENO[1]} "${@}" >&3 7 | 8 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 9 | -------------------------------------------------------------------------------- /src/utils/log/title.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Show title. 3 | ## 4 | ## Params: 5 | ## *: {String} Message. 6 | 7 | # Start 8 | local t=" $@ " 9 | local l=$(@str.len "${t}") 10 | 11 | @log.line $l '-' '+-' '-+' 12 | @log "| ${t} $(@style)|" 13 | @log.line $l '-' '+-' '-+' 14 | @log 15 | 16 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 17 | -------------------------------------------------------------------------------- /src/utils/log/waiting.xsh: -------------------------------------------------------------------------------- 1 | ## timeout [message] 2 | ## Print the {message} every 0.25 seconds until the {timeout} is reached. 3 | ## This script blocks the execution (`sleep`) until the {timeout} is reached. 4 | ## Note: Overlay {message} can not be cancelled. 5 | ## 6 | ## Params: 7 | ## timeout: {Integer} Timeout in seconds. Should be bigger than 1. 8 | ## message: {String} Message to print every 0.25 seconds. 9 | ## Optional. Default: "Please wait...". 10 | 11 | local timeout=$1 12 | local message="${2:-Please wait...}" 13 | local to=$((timeout - 1)) 14 | local s 15 | local ms 16 | 17 | [[ $to -lt 1 ]] && @throw.invalidParam timeout 18 | 19 | for s in $(seq 0 $to); do 20 | for ms in $(seq 0 25 75); do 21 | ( 22 | sleep $s.$ms 23 | echo -e "\r" >&3 24 | @log "$message" 25 | ) & 26 | done 27 | done 28 | 29 | sleep $timeout 30 | echo -e "\r\n\n" >&3 31 | 32 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 33 | -------------------------------------------------------------------------------- /src/utils/log/warn.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Print at stderr. 3 | ## 4 | ## Params: 5 | ## *: {String} Text to print. 6 | 7 | @log "$@" 3>&2 8 | 9 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 10 | -------------------------------------------------------------------------------- /src/utils/mktemp.xsh: -------------------------------------------------------------------------------- 1 | ## [create [type]] 2 | ## Create a temporary file or directory, safely, and print its name. 3 | ## It works like "mktemp", but the result is saved in ${BASHX_APP_TMP_PATH}. 4 | ## When the script finishes running, the ${BASHX_APP_TMP_PATH} directory is 5 | ## completely removed along with all its contents. 6 | ## 7 | ## Params: 8 | ## create: {Boolean} Do not create anything; merely print a name. 9 | ## Optional. Default: true. 10 | ## type: {Constant} Creta File or Directory. 11 | ## Options: 12 | ## f || file: Create file. 13 | ## d || dir || directory: Create directory. 14 | ## Optional. Default: "f". 15 | ## 16 | ## Out: Print its name. 17 | 18 | local create="${1:-true}" 19 | local type="${2:-f}" 20 | 21 | local p="${BASHX_APP_TMP_PATH}$(env LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | head -c 10)XXXXXX" 22 | local cmd="-q" 23 | 24 | case "${type}" in 25 | f|file) 26 | # Default is "file", do nothing. 27 | ;; 28 | d|dir|directory) 29 | cmd="${cmd} -d" 30 | ;; 31 | *) 32 | # Default is "file", do nothing. 33 | ;; 34 | esac 35 | 36 | if ${create}; then 37 | cmd="${cmd} -u" 38 | fi 39 | 40 | eval "mktemp ${cmd} ${p} 2>/dev/null || mktemp ${cmd} -t ${p} 2>/dev/null" 41 | 42 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 43 | -------------------------------------------------------------------------------- /src/utils/now/date.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Current date. 3 | ## Format: %Y-%m-%d 4 | ## 5 | ## Out: {String} Date. 6 | 7 | date '+%Y-%m-%d' 8 | 9 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 10 | -------------------------------------------------------------------------------- /src/utils/now/dateTime.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Current date and time. 3 | ## Format: %Y-%m-%d %H:%M:%S 4 | ## 5 | ## Out: {String} Date and time. 6 | 7 | date '+%Y-%m-%d %H:%M:%S' 8 | 9 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 10 | -------------------------------------------------------------------------------- /src/utils/now/time.xsh: -------------------------------------------------------------------------------- 1 | ## 2 | ## Current time. 3 | ## Format: %H:%M:%S 4 | ## 5 | ## Out: {String} Time. 6 | 7 | date '%H:%M:%S' 8 | 9 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 10 | -------------------------------------------------------------------------------- /src/utils/on/exit.xsh: -------------------------------------------------------------------------------- 1 | ## @ 2 | ## Add action to execute @on.exit callback. 3 | ## You can access current exit code from ${EXIT_CODE} variable. 4 | ## 5 | ## Params: 6 | ## @: Command to execute on APP exit. 7 | ## 8 | ## Example: 9 | ## _onExit() { 10 | ## local code=${1} 11 | ## # ... 12 | ## } 13 | ## 14 | ## @on.exit '_onExit "${EXIT_CODE}"' 15 | 16 | local cmd="$@" 17 | 18 | if [[ ! -z "${cmd}" ]]; then 19 | [ -z "${BX_ON_EXIT}" ] || BX_ON_EXIT="${BX_ON_EXIT};" 20 | BX_ON_EXIT="${BX_ON_EXIT}( ${cmd} )" 21 | fi 22 | 23 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 24 | -------------------------------------------------------------------------------- /src/utils/on/stderr.xsh: -------------------------------------------------------------------------------- 1 | ## @ 2 | ## Add action to execute on stderr callback. 3 | ## You can access current line data from ${OUT_LINE} variable. 4 | ## 5 | ## Params: 6 | ## @: Command to execute on each stderr line. 7 | ## 8 | ## Example: 9 | ## _onStdErr() { 10 | ## local line="$1" 11 | ## echo "++ ${line}" 12 | ## } 13 | ## 14 | ## @on.stderr '_onStdErr "${OUT_LINE}"' 15 | 16 | # Not implemented 17 | exit 1 18 | 19 | local cmd="$@" 20 | 21 | if [[ ! -z "${cmd}" ]]; then 22 | [ -z "${BX_ON_STDERR}" ] || BX_ON_STDERR="${BX_ON_STDERR};" 23 | BX_ON_STDERR="${BX_ON_STDERR}( ${cmd} )" 24 | fi 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/on/stdinfo.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Add action to execute on stdinfo callback. 3 | ## You can access current line data from ${OUT_LINE} variable. 4 | ## 5 | ## Params: 6 | ## *: Command to execute on each stdinfo line. 7 | ## 8 | ## Example: 9 | ## _onStdInfo() { 10 | ## local line="$1" 11 | ## echo "++ ${line}" 12 | ## } 13 | ## 14 | ## @on.stdinfo '_onStdInfo "${OUT_LINE}"' 15 | 16 | # Not implemented 17 | exit 1 18 | 19 | local cmd="$@" 20 | 21 | if [[ ! -z "${cmd}" ]]; then 22 | [ -z "${BX_ON_STDINFO}" ] || BX_ON_STDINFO="${BX_ON_STDINFO};" 23 | BX_ON_STDINFO="${BX_ON_STDINFO}( ${cmd} )" 24 | fi 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/on/stdout.xsh: -------------------------------------------------------------------------------- 1 | ## @ 2 | ## Add action to execute on stdout callback. 3 | ## You can access current line data from ${OUT_LINE} variable. 4 | ## 5 | ## Params: 6 | ## @: Command to execute on each stdout line. 7 | ## 8 | ## Example: 9 | ## _onStdOut() { 10 | ## local line="$1" 11 | ## echo "++ ${line}" 12 | ## } 13 | ## 14 | ## @on.stdout '_onStdOut "${OUT_LINE}"' 15 | 16 | # Not implemented 17 | exit 1 18 | 19 | local cmd="$@" 20 | 21 | if [[ ! -z "${cmd}" ]]; then 22 | [ -z "${BX_ON_STDOUT}" ] || BX_ON_STDOUT="${BX_ON_STDOUT};" 23 | BX_ON_STDOUT="${BX_ON_STDOUT}( ${cmd} )" 24 | fi 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/random.xsh: -------------------------------------------------------------------------------- 1 | ## [len] 2 | ## Generate random string. 3 | ## 4 | ## Out: {String} Random string. 5 | 6 | local len="${1:-10}" 7 | 8 | local p="$(env LC_ALL=C tr -dc "a-zA-Z0-9" < /dev/urandom 2>/dev/null | head -c ${len})" 9 | 10 | if [[ -z "${p}" ]]; then 11 | shuf -zer -n${len} {A..Z} {a..z} {0..9} 2>/dev/null 12 | else 13 | echo "${p}" 14 | fi 15 | 16 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 17 | -------------------------------------------------------------------------------- /src/utils/required.xsh: -------------------------------------------------------------------------------- 1 | ## cmd* 2 | ## Check for required software. 3 | ## 4 | ## Params: 5 | ## cmd* {String} List of commands to check. 6 | ## 7 | ## Usage example: 8 | ## @required docker docker-compose composer npm 9 | 10 | local cmds=( $@ ) 11 | local cmd 12 | 13 | for cmd in ${cmds[@]}; do 14 | if ! command -v ${cmd} >/dev/null 2>&1 15 | then 16 | @app.error "I require [${cmd}] but it's not installed. Aborting." 17 | fi 18 | done 19 | 20 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 21 | -------------------------------------------------------------------------------- /src/utils/rootValidate.xsh: -------------------------------------------------------------------------------- 1 | ## [*] 2 | ## Check if running as ROOT, or exit from script. 3 | ## 4 | ## Params: 5 | ## *: {String} Message. 6 | ## Optional. Default: "This script must be run as root!". 7 | 8 | if ! @isRoot ; then 9 | local message="${*:-This script must be run as root!}" 10 | @app.error "$message" 11 | fi 12 | 13 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 14 | -------------------------------------------------------------------------------- /src/utils/screen/width.xsh: -------------------------------------------------------------------------------- 1 | ## [default_width] 2 | ## Screen width. 3 | ## 4 | ## Params: 5 | ## default: {Integer} Screen width default if `tput` is not exists. 6 | ## Optional. Default: ${BX_BASHX_APP_WIDTH} 7 | ## 8 | ## Out: {Integer} Screen width. 9 | 10 | local default_width=${1:-${BASHX_APP_WIDTH}} 11 | 12 | if command -v tput >/dev/null 2>&1; then 13 | tput cols 2>/dev/null || echo ${default_width} 14 | else 15 | echo ${default_width} 16 | fi 17 | 18 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 19 | -------------------------------------------------------------------------------- /src/utils/sed.xsh: -------------------------------------------------------------------------------- 1 | ## {file} {regexp} 2 | ## `sed` command alias compatible between Mac & Linux. 3 | ## 4 | ## Params: Parameters for following syntax: 5 | ## sed -i -e "{regexp}" "{file}" 6 | 7 | local file="$1" 8 | local regexp="$2" 9 | 10 | if ${BX_OS_IS_MAC}; then 11 | sed -i '' -e "${regexp}" "${file}" 12 | else 13 | sed -i -e "${regexp}" "${file}" 14 | fi 15 | 16 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 17 | -------------------------------------------------------------------------------- /src/utils/str/escape.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Escape string for Bash. 3 | ## 4 | ## Params: 5 | ## *: {String} String to escape for Bash. 6 | ## 7 | ## Out: {String} Escaped string. 8 | 9 | local str="$@" 10 | 11 | if [[ ! -z "${str}" ]]; then 12 | printf '%q' "${str}" 13 | fi 14 | 15 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 16 | -------------------------------------------------------------------------------- /src/utils/str/in.xsh: -------------------------------------------------------------------------------- 1 | ## src search [case_sensitive] 2 | ## String contains substring? 3 | ## 4 | ## Params: 5 | ## src: {String} String where search. 6 | ## search: {String} Substring to search. 7 | ## case_sensitive: {Boolean} TRUE for case sensitive. 8 | ## Optional. Default: true. 9 | ## 10 | ## Return: 0 if contains substring, 1 if not contains substring. 11 | 12 | local src="$1" 13 | local search="$2" 14 | local case_sensitive=${3:-true} 15 | 16 | if \ 17 | [[ ! -z "${src}" ]] && \ 18 | [[ ! -z "${search}" ]] 19 | then 20 | @str.pos "${src}" "${search}" ${case_sensitive} >/dev/null 2>&1 21 | return $? 22 | fi 23 | 24 | return 1 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/str/len.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## String length. 3 | ## 4 | ## Params: 5 | ## *: {String} Text. 6 | ## 7 | ## Out: {Integer} String length. 8 | 9 | local str="$(@style.clean "$@")" 10 | echo ${#str} 11 | 12 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 13 | -------------------------------------------------------------------------------- /src/utils/str/ltrim.xsh: -------------------------------------------------------------------------------- 1 | ## text [to_remove] 2 | ## Remove starting spaces from text. 3 | ## 4 | ## Params: 5 | ## text: {String} Text. 6 | ## to_remove: {String} String to remove. 7 | ## Optional. Default: " ". 8 | ## 9 | ## Out: {String} Trimed text. 10 | 11 | local text="$1" 12 | local to_remove="${2:- }" 13 | 14 | if \ 15 | [[ ! -z "${text}" ]] && \ 16 | [[ ! -z "${to_remove}" ]] 17 | then 18 | local old='' 19 | 20 | while [[ ${#text} -ne ${#old} ]]; do 21 | old="${text}" 22 | text="${text#$to_remove}" # LTRIM 23 | done 24 | fi 25 | 26 | echo -n "${text}" 27 | 28 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 29 | -------------------------------------------------------------------------------- /src/utils/str/pos.xsh: -------------------------------------------------------------------------------- 1 | ## src search [case_sensitive] 2 | ## String position. 3 | ## 4 | ## Params: 5 | ## src: {String} String where search. 6 | ## search: {String} String to search. 7 | ## case_sensitive: {Boolean} True for case sensitive. 8 | ## Optional. Default: true. 9 | ## 10 | ## Out: {Integer|NULL} String position or NULL if not found. 11 | ## Return: 0 on fonud, 1 on not found. 12 | 13 | local src="$1" 14 | local search="$2" 15 | local case_sensitive=${3:-true} 16 | 17 | if \ 18 | [[ -z "${src}" ]] || \ 19 | [[ -z "${search}" ]] 20 | then 21 | # Empty variable 22 | return 1 23 | fi 24 | 25 | if ! ${case_sensitive}; then 26 | # Case insensitive 27 | src="$(@str.toLower "${src}")" 28 | search="$(@str.toLower "${search}")" 29 | fi 30 | 31 | local tmp="${src%${search}*}" 32 | local index=${#tmp} 33 | 34 | if [[ ${index} -eq ${#src} ]]; then 35 | # Not found 36 | return 1 37 | else 38 | # Found 39 | echo ${index} 40 | return 0 41 | fi 42 | 43 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 44 | -------------------------------------------------------------------------------- /src/utils/str/repeat.xsh: -------------------------------------------------------------------------------- 1 | ## count str 2 | ## Repeat string. 3 | ## 4 | ## Params: 5 | ## count: {Integer} Number of repetitions. 6 | ## str: {String} String to repeat. 7 | ## 8 | ## Out: {String} Repeated string. 9 | 10 | local count=$1 11 | local str="$2" 12 | 13 | if [ ! -z "${count}" ] && [[ ${count} -gt 0 ]] && [ ! -z "${str}" ]; then 14 | local result='' 15 | 16 | while [[ ${count} -gt 0 ]]; do 17 | result="${result}${str}" 18 | count=$((count - 1)) 19 | done 20 | 21 | echo "${result}" 22 | fi 23 | 24 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 25 | -------------------------------------------------------------------------------- /src/utils/str/replace.xsh: -------------------------------------------------------------------------------- 1 | ## src search replace [ignore_case] 2 | ## Replace string. 3 | ## 4 | ## Params: 5 | ## src: {String} String where replace. 6 | ## search: {String} Search RegExp string. 7 | ## replace: {String} Replace. 8 | ## ignore_case: {Boolean} True to ignore case. 9 | ## Optional. Default: true. 10 | ## 11 | ## Out: {String} Result string. 12 | 13 | local src="$1" 14 | local search="$2" 15 | local replace="$3" 16 | local ignore_case=${4:-true} 17 | 18 | if [[ -z "${src}" ]]; then 19 | echo '' 20 | elif [[ -z "${search}" ]]; then 21 | echo "${src}" 22 | else 23 | local options='g' 24 | ${ignore_case} && options="${options}i" 25 | 26 | perl -C -Mutf8 -pe "s/${search}/${replace}/${options}" <<<"${src}" 27 | fi 28 | 29 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 30 | -------------------------------------------------------------------------------- /src/utils/str/rtrim.xsh: -------------------------------------------------------------------------------- 1 | ## text [to_remove] 2 | ## Remove ending spaces from text. 3 | ## 4 | ## Params: 5 | ## text: {String} Text. 6 | ## to_remove: {String} String to remove. 7 | ## Optional. Default: " ". 8 | ## 9 | ## Out: {String} Trimed text. 10 | 11 | local text="$1" 12 | local to_remove="${2:- }" 13 | 14 | if \ 15 | [[ ! -z "${text}" ]] && \ 16 | [[ ! -z "${to_remove}" ]] 17 | then 18 | local old='' 19 | 20 | while [[ ${#text} -ne ${#old} ]]; do 21 | old="${text}" 22 | text="${text%$to_remove}" # RTRIM 23 | done 24 | fi 25 | 26 | echo -n "${text}" 27 | 28 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 29 | -------------------------------------------------------------------------------- /src/utils/str/sub.xsh: -------------------------------------------------------------------------------- 1 | ## src limit [offset] 2 | ## Sub string. 3 | ## 4 | ## Params: 5 | ## src: {String} String to cut. 6 | ## limit: {Integer} Limit. 7 | ## offset: {Integer} Offset. 8 | ## Optional. Default: 0. 9 | ## 10 | ## Out: {String} Result string 11 | 12 | local src="$1" 13 | local limit=$2 14 | local offset=$3 15 | 16 | if \ 17 | [[ -z "${offset}" ]] || \ 18 | [[ ${offset} -eq 0 ]] || \ 19 | ! @isNumber "${offset}" 20 | then 21 | echo "${src:0:${limit}}" 22 | else 23 | echo "${src:${offset}:${limit}}" 24 | fi 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/str/toLower.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## String to lower case. 3 | ## 4 | ## Params: 5 | ## *: {String} String to convert. 6 | ## 7 | ## Out: {String} Result string. 8 | 9 | local text="$@" 10 | 11 | # https://superuser.com/a/642932/84212 12 | [ ! -z "${text}" ] && echo "${text}" | tail -n +1 | tr '[A-Z]' '[a-z]' 13 | 14 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 15 | -------------------------------------------------------------------------------- /src/utils/str/toUpper.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## String to upper case. 3 | ## 4 | ## Params: 5 | ## *: {String} String to convert. 6 | ## 7 | ## Out: {String} Result string. 8 | 9 | local text="$@" 10 | 11 | # https://superuser.com/a/642932/84212 12 | [ ! -z "${text}" ] && echo "${text}" | tail -n +1 | tr '[a-z]' '[A-Z]' 13 | 14 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 15 | -------------------------------------------------------------------------------- /src/utils/str/trim.xsh: -------------------------------------------------------------------------------- 1 | ## text [to_remove] 2 | ## Remove starting and ending spaces from text. 3 | ## 4 | ## Params: 5 | ## text: {String} Text. 6 | ## to_remove: {String} String to remove. 7 | ## Optional. Default: " ". 8 | ## 9 | ## Out: {String} Trimed text. 10 | 11 | local text="$1" 12 | local to_remove="${2:- }" 13 | 14 | if [ ! -z "${text}" ] && [ ! -z "${to_remove}" ]; then 15 | local old='' 16 | 17 | while [[ ${#text} -ne ${#old} ]]; do 18 | old="${text}" 19 | text="${text#$to_remove}" # LTRIM 20 | text="${text%$to_remove}" # RTRIM 21 | done 22 | fi 23 | 24 | echo -n "${text}" 25 | 26 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 27 | -------------------------------------------------------------------------------- /src/utils/style.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Change screen print style. 3 | ## 4 | ## Params: 5 | ## *: Style strings. 6 | ## The style separator can be: 7 | ## - : (two points) 8 | ## - = (equal) 9 | ## Simple: 10 | ## - bold: Bold text. 11 | ## - underline: Underline. 12 | ## - reverse | negative: Invert color. 13 | ## - hidden | hide: Hide text. 14 | ## - show | visible: Show hidden text. 15 | ## - dim: Gray style. 16 | ## - blink: Flashing text. 17 | ## - normal: Text without bold format. 18 | ## - reset: Reset all styles to system default. 19 | ## - default: Reset all styles to APP style. 20 | ## Colors: Use "color[:=]*", where "*" can be: 21 | ## - black 22 | ## - blue 23 | ## - blue-light 24 | ## - cyan 25 | ## - cyan-light 26 | ## - gray 27 | ## - gray-light 28 | ## - green 29 | ## - green-light 30 | ## - magenta | purple 31 | ## - magenta-light 32 | ## - red 33 | ## - red-light 34 | ## - yellow | coffe 35 | ## - yellow-light 36 | ## - white 37 | ## - default | normal | auto: Default APP color. 38 | ## - [A Number between 0 and 255]: Print custom color. 39 | ## Background: Use "background[:=]*" or "bg[:=]*", where "*" can be: 40 | ## - black 41 | ## - blue 42 | ## - blue-light 43 | ## - cyan 44 | ## - cyan-light 45 | ## - gray 46 | ## - gray-light 47 | ## - green 48 | ## - green-light 49 | ## - magenta | purple 50 | ## - magenta-light 51 | ## - red 52 | ## - red-light 53 | ## - yellow | coffe 54 | ## - yellow-light 55 | ## - white 56 | ## - system | normal | auto: Default system background color. 57 | ## - [A Number between 0 and 255]: Print custom color. 58 | ## Styles: Use as "style[:=]status": 59 | ## - underline: Underline. 60 | ## - on | true | 1: Enable underline. 61 | ## - off | false | 0: Disable underline. 62 | ## - bold: Bold. 63 | ## - on | true | 1: Enable bold. 64 | ## - off | false | 0: Disable bold. 65 | ## - dim: Gray style. 66 | ## - on | true | 1: Enable dim. 67 | ## - off | false | 0: Disable dim. 68 | ## - blink: Flashing text. 69 | ## - on | true | 1: Enable blink. 70 | ## - off | false | 0: Disable blink. 71 | ## - reverse | negative: Invert color. 72 | ## - on | true | 1: Enable negative. 73 | ## - off | false | 0: Disable negative. 74 | ## - display: Show or hide text. 75 | ## - visible | show | true | 1: Show text. 76 | ## - hidden | none | hide | false | 0: Hide text. 77 | ## 78 | ## Out: {String} Style console string. 79 | ## 80 | ## Global Variables: 81 | ## BASHX_COLORS_DISABLED: {Integer} Set vale to '1' to diable this function 82 | ## and disable output colors. 83 | ## 84 | ## Examples: 85 | ## @log "normal color $(@style color:red)text in red $(@style color:black background:yellow)black color$(@style) normal color" 86 | ## # Restore default APP colors 87 | ## @style | @style default 88 | ## # Set gray color as background color for next output 89 | ## @style background gray 90 | ## @log "$(@style color:red bold underline:on)Title$(@style underline:off):$(@style normal dim) Description..." 91 | 92 | if \ 93 | ${BASHX_APP_COLORS_ENABLED} && \ 94 | [[ "${BASHX_COLORS_DISABLED}" != '1' ]] 95 | then 96 | local OIFS="$IFS" 97 | 98 | # No parameters 99 | local prms="$@" 100 | if [[ $# -eq 0 ]]; then 101 | # Default color 102 | prms='default' 103 | fi 104 | 105 | # Styles 106 | local c='' 107 | IFS=' ' prms=(${prms}) IFS="$OIFS" 108 | 109 | for p in ${prms[@]} ; do 110 | # Style code 111 | local y='' 112 | # Split 113 | IFS='[=:]' p=(${p}) IFS="$OIFS" 114 | # Parts 115 | local s="${p[0]}" 116 | local v="${p[1]}" 117 | 118 | # Default 119 | if \ 120 | [[ -z "$v" ]] && \ 121 | [[ "$p" == 'default' ]] 122 | then 123 | # Reset style 124 | if [[ -z "$c" ]]; then 125 | c='0' 126 | else 127 | c="$c;0" 128 | fi 129 | # Default color 130 | s='color' 131 | v="${BASHX_APP_COLOR_DEFAULT}" 132 | fi 133 | 134 | if [[ ! -z "$v" ]]; then 135 | if \ 136 | [[ "$s" == 'color' ]] && \ 137 | ( \ 138 | [[ "$v" == 'default' ]] || \ 139 | [[ "$v" == 'normal' ]] || \ 140 | [[ "$v" == 'auto' ]] \ 141 | ) 142 | then 143 | if \ 144 | [[ -z "${BASHX_APP_COLOR_DEFAULT}" ]] || \ 145 | [[ "${BASHX_APP_COLOR_DEFAULT}" == 'default' ]] || \ 146 | [[ "${BASHX_APP_COLOR_DEFAULT}" == 'normal' ]] || \ 147 | [[ "${BASHX_APP_COLOR_DEFAULT}" == 'auto' ]] 148 | then 149 | # Invalid default or default not defined 150 | y='0' 151 | # Invalidate case 152 | v='' 153 | s='' 154 | else 155 | # Default color 156 | v="${BASHX_APP_COLOR_DEFAULT}" 157 | fi 158 | fi 159 | 160 | case "$s" in 161 | 'color') 162 | case "$v" in 163 | 'black') y='30' ;; 164 | 'blue') y='34' ;; 165 | 'blue-light') y='94' ;; 166 | 'cyan') y='36' ;; 167 | 'cyan-light') y='96' ;; 168 | 'gray') y='90' ;; 169 | 'gray-light') y='37' ;; 170 | 'green') y='32' ;; 171 | 'green-light') y='92' ;; 172 | 'magenta' | 'purple') y='35' ;; 173 | 'magenta-light') y='95' ;; 174 | 'red') y='31' ;; 175 | 'red-light') y='91' ;; 176 | 'yellow' | 'coffe') y='33' ;; 177 | 'yellow-light') y='93' ;; 178 | 'white') y='97' ;; 179 | # Color (0 - 255) 180 | [0-9]) y="38;5;${v}" ;; 181 | esac 182 | ;; 183 | 'background' | 'bg') 184 | case "$v" in 185 | 'black') y='40' ;; 186 | 'blue') y='44' ;; 187 | 'blue-light') y='104' ;; 188 | 'cyan') y='46' ;; 189 | 'cyan-light') y='106' ;; 190 | 'gray') y='100' ;; 191 | 'gray-light') y='47' ;; 192 | 'green') y='42' ;; 193 | 'green-light') y='102' ;; 194 | 'magenta' | 'purple') y='45' ;; 195 | 'magenta-light') y='105' ;; 196 | 'red') y='41' ;; 197 | 'red-light') y='101' ;; 198 | 'yellow' | 'coffe') y='43' ;; 199 | 'yellow-light') y='103' ;; 200 | 'white') y='107' ;; 201 | "system" | 'normal' | 'auto') y='49' ;; 202 | # Color (0-255) 203 | [0-9]) y="48;5;${v}" ;; 204 | esac 205 | ;; 206 | 'underline') 207 | case "$v" in 208 | 'on' | 'true' | '1') y='4' ;; 209 | 'off' | 'false' | '0') y='24' ;; 210 | esac 211 | ;; 212 | 'bold') 213 | case "$v" in 214 | 'on' | 'true' | '1') y='1' ;; 215 | 'off' | 'false' | '0') y='21' ;; 216 | esac 217 | ;; 218 | 'dim') 219 | # Gray stile 220 | case "$v" in 221 | 'on' | 'true' | '1') y='2' ;; 222 | 'off' | 'false' | '0') y='22' ;; 223 | esac 224 | ;; 225 | 'blink') 226 | # Flashing text 227 | case "$v" in 228 | 'on' | 'true' | '1') y='5' ;; 229 | 'off' | 'false' | '0') y='25' ;; 230 | esac 231 | ;; 232 | 'reverse' | 'negative') 233 | case "$v" in 234 | 'on' | 'true' | '1') y='7' ;; 235 | 'off' | 'false' | '0') y='27' ;; 236 | esac 237 | ;; 238 | 'display') 239 | case "$v" in 240 | 'visible' | 'show' | 'true' | '1') y='28' ;; 241 | 'hidden' | 'none' | 'hide' | 'false' | '0') y='8' ;; 242 | esac 243 | ;; 244 | esac 245 | else 246 | case "$p" in 247 | 'bold') y='1' ;; 248 | 'underline') y='4' ;; 249 | 'reverse' | 'negative') y='7' ;; 250 | 'hidden' | 'hide') y='8' ;; 251 | 'show' | 'visible') y='28' ;; 252 | # Gray stile 253 | 'dim') y='2' ;; 254 | # Flashing text 255 | 'blink') y='5' ;; 256 | # No bold 257 | 'normal') y='21' ;; 258 | # Reset all styles 259 | 'reset') y='0' ;; 260 | esac 261 | fi 262 | 263 | if [[ ! -z "$y" ]]; then 264 | # Append style 265 | if [[ -z "$c" ]]; then 266 | c="$y" 267 | else 268 | c="$c;$y" 269 | fi 270 | fi 271 | done 272 | 273 | if [[ ! -z "${c}" ]]; then 274 | echo -en "${BX_KEY_ESC}[${c}m" 275 | fi 276 | fi 277 | 278 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 279 | -------------------------------------------------------------------------------- /src/utils/style/clean.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Remove color codes (special characters). 3 | ## 4 | ## Params: 5 | ## *: {String} Text. 6 | ## 7 | ## Out: {String} Text without format. 8 | 9 | # http://www.commandlinefu.com/commands/view/3584/remove-color-codes-special-characters-with-sed 10 | 11 | local pr 12 | 13 | $BX_OS_IS_MAC && pr='E' || pr='r' 14 | 15 | sed -${pr} "s/${BX_KEY_ESC}\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" <<<"$@" 16 | 17 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 18 | -------------------------------------------------------------------------------- /src/utils/sudo.xsh: -------------------------------------------------------------------------------- 1 | ## path command 2 | ## Run with `sudo` if necessary. 3 | ## If {path} is not writable then run with `sudo`. 4 | ## 5 | ## Params: 6 | ## path: {String} Related path for write permission is granted. 7 | ## command: {String} Command to execute. 8 | ## 9 | ## Return: Executed {command} exit code. 10 | 11 | local path="$1" 12 | local command="$2" 13 | 14 | if ${BX_OS_IS_MINGW} || [ -w "${path}" ]; then 15 | ( set -ex 16 | bash -c "${command}" 17 | ) 18 | return $? 19 | else 20 | @required sudo 21 | ( set -ex 22 | sudo bash -c "${command}" 23 | ) 24 | return $? 25 | fi 26 | 27 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 28 | -------------------------------------------------------------------------------- /src/utils/throw.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Throw illegal error and exit. 3 | ## 4 | ## Params: 5 | ## *: {String} Error message. 6 | 7 | BX_APP_EXIT_ILLEGAL_ERROR=true 8 | 9 | ${BASHX_APP_COLORS_ENABLED} && local err_char='❌❌❌' || local err_char='@@@' 10 | 11 | echo >&2 12 | @log.alert "${err_char} Error! ${err_char}" 13 | @app.error "$@" true 1 14 | 15 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 16 | -------------------------------------------------------------------------------- /src/utils/throw/invalidParam.xsh: -------------------------------------------------------------------------------- 1 | ## variable_name [note] 2 | ## Throw illegal error and exit. 3 | ## 4 | ## Params: 5 | ## variable_name: {String} Parameter/Variable name. 6 | ## note: {String} Extra note about the error. 7 | ## Optional. 8 | 9 | # Read variable value. Should be the first line of this function. 10 | local variable_value="$(eval "echo -e -n \"\$${1}\"")" 11 | 12 | local variable_name="$1" 13 | local note="$2" 14 | local script_name="${FUNCNAME[2]}" 15 | local msg 16 | local line 17 | 18 | if [[ "${script_name}" == 'source' ]]; then 19 | script_name="${FUNCNAME[3]}" 20 | fi 21 | 22 | if [[ -z "${variable_value}" ]]; then 23 | msg="[${variable_name}] can not be emtpy" 24 | else 25 | msg="Invalid value for [${variable_name}]: '${variable_value}'" 26 | fi 27 | 28 | if [[ ! -z "${note}" ]]; then 29 | echo -e "${note}" | while read line ; do 30 | msg="${msg}\n > ${line}" 31 | done 32 | fi 33 | 34 | @throw "Invalid call of [${script_name}].\n > ${msg}" 35 | 36 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 37 | -------------------------------------------------------------------------------- /src/utils/throw/notImplemented.xsh: -------------------------------------------------------------------------------- 1 | ## [note] 2 | ## Throw not implemented error and exit. 3 | ## 4 | ## Params: 5 | ## note: {String} Extra note about the error. 6 | ## Optional. 7 | 8 | local note="$1" 9 | local script_name="${FUNCNAME[2]}" 10 | local msg 11 | local line 12 | 13 | if [ ! -z "${note}" ]; then 14 | echo -e "${note}" | while read line ; do 15 | msg="${msg}\n > ${line}" 16 | done 17 | fi 18 | 19 | @throw "@@@ [${script_name}] NOT IMPLEMENTED! @@@\n[${script_name}] is not implemented!${msg}" 20 | 21 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 22 | -------------------------------------------------------------------------------- /src/utils/usage.xsh: -------------------------------------------------------------------------------- 1 | ## src [prefix] 2 | ## Print basic script usage. 3 | ## 4 | ## Params: 5 | ## src: {String} File to read usage. 6 | ## prefix: {String} Usage prefix. 7 | ## Optional. Default: ${BX_SCRIPT_FILE_NAME}. 8 | ## cmd: Optional. Default: File name. 9 | ## 10 | ## Out: {String} Usage text. 11 | 12 | local src="$1" 13 | local prefix="${2:-${BX_SCRIPT_FILE_NAME}}" 14 | local cmd="${3:-$(@file.name "${src}" true)}" 15 | 16 | local lp=' ' 17 | local lpl=${#lp} 18 | local first_line=true 19 | local sd="$(@style)" 20 | local info 21 | 22 | if [ -z "${src}" ] || [ ! -f "${src}" ]; then 23 | @throw "Invalid file '${src}'" 24 | fi 25 | 26 | if [ ! -z "${prefix}" ]; then 27 | prefix="$(@style color:red)${prefix}" 28 | fi 29 | 30 | # Get comments from file 31 | egrep "^${BASHX_DOC_MARK}" "${src}" | egrep -v "^${BASHX_DOC_MARK}#" | while read line 32 | do 33 | if ${first_line}; then 34 | first_line=false 35 | @log " ${prefix} $(@style color:green)${cmd}${sd} $(@str.replace "${line}" "^${BASHX_DOC_MARK}\\s*" '')" 36 | else 37 | @log "$(@str.replace "${line}" "^${BASHX_DOC_MARK}\\s" "${sd}${lp}")" 38 | fi 39 | done 40 | 41 | @log 42 | 43 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 44 | -------------------------------------------------------------------------------- /src/utils/usage/file.xsh: -------------------------------------------------------------------------------- 1 | ## src [prefix] 2 | ## Print basic main script usage. 3 | ## 4 | ## Params: 5 | ## src: {String} File to read usage. 6 | ## prefix: {String} Usage prefix. 7 | ## Optional. Default: ${BX_SCRIPT_FILE_NAME}. 8 | ## 9 | ## Out: {String} Usage text. 10 | 11 | local src="$1" 12 | local prefix="${2:-$BX_SCRIPT_FILE_NAME} " 13 | local line 14 | local cmd="$(@file.name "${src}" true)" 15 | 16 | if [[ "${cmd}" == _* ]]; then 17 | return 0 18 | fi 19 | 20 | egrep "^\\s*${BX_ACTION_PREFIX}\.[^()]+\\(\\)\\s*\\{" "${src}" \ 21 | | egrep -v "^\\s*${BX_ACTION_PREFIX}\._" \ 22 | | while read line 23 | do 24 | # Script name & action 25 | line=" $(@style color:red)${prefix}$(@style color:green)$(@str.replace "${line}" "^\\s*${BX_ACTION_PREFIX/\@/\\@}\\." '')" 26 | # Parameters 27 | line="$(@str.replace "${line}" '\(\)\s*\{\s*#*\s*' "$(@style) ")" 28 | # Doc Lines 29 | line="$(@str.replace "${line}" '\s*\\n\\t\s*' "\n ")" 30 | line="$(@str.replace "${line}" '\s*\\n\s*' "\n ")" 31 | # Tab > Space 32 | @log "$(@str.replace "${line}" '\s*\\t\s*' ' ')" 33 | # Space 34 | @log 35 | done 36 | 37 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 38 | -------------------------------------------------------------------------------- /src/utils/user/choice.xsh: -------------------------------------------------------------------------------- 1 | ## message options [default] 2 | ## User choice. 3 | ## 4 | ## Params: 5 | ## message: {String} Message. 6 | ## options: {String} Options. Chars separated by a space. 7 | ## default: {Char} Default value on non user input or invalid choice. 8 | ## Optional. Default: "". 9 | ## 10 | ## Out: User selection or Default. 11 | ## 12 | ## Usage example: 13 | ## @user.choice "Message..." "a b c" "b" 14 | 15 | # Message 16 | local message="$1" 17 | # Options 18 | local options=($2) 19 | # Default result 20 | local default="$3" 21 | 22 | [ -z "$message" ] && @throw.invalidParam message 23 | [ -z "$options" ] && @throw.invalidParam options 24 | 25 | local result="$default" 26 | local user_input 27 | 28 | # Read 29 | if ${BX_TTY}; then 30 | read -n 1 -p "$(@style)${BASHX_APP_PRINT_PREFIX} ${message} [${options}]: " user_input >&3 31 | else 32 | @log.warn 'Not at TTY! choice cancelled...' 33 | echo "${default}" 34 | return 254 35 | fi 36 | echo >&3 37 | 38 | # Validate input 39 | user_input=$(@str.trim "${user_input}") 40 | if [[ ! -z "${user_input}" ]]; then 41 | for option in ${options[@]}; do 42 | if [[ "${option}" == "${user_input}" ]]; then 43 | # Valid input 44 | result="${option}" 45 | fi 46 | done 47 | fi 48 | 49 | # Result 50 | echo "${result}" 51 | 52 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 53 | -------------------------------------------------------------------------------- /src/utils/user/confirm.xsh: -------------------------------------------------------------------------------- 1 | ## [msg] 2 | ## User confirm. 3 | ## 4 | ## Params: 5 | ## msg: {String} Message. 6 | ## Optional. Default: "Confirm?". 7 | ## 8 | ## Return: 9 | ## 0 if user confirm. 10 | ## 1 if user not confirm. 11 | ## 254 if no TTY. 12 | 13 | local msg="${1:-Confirm?}" 14 | local choice 15 | local result=3 16 | 17 | echo -n "$(@style)${BASHX_APP_PRINT_PREFIX} ${msg} (y/n) " >&3 18 | 19 | if ${BX_TTY}; then 20 | while [[ ${result} -gt 1 ]] ; do 21 | read -s -n 1 choice >&3 || true 22 | 23 | case "${choice}" in 24 | y|Y ) result=0 ;; 25 | n|N ) result=1 ;; 26 | esac 27 | done 28 | else 29 | @log.warn 'Not at TTY! confirm cancelled...' 30 | return 254 31 | fi 32 | 33 | if [[ ${result} -eq 0 ]] ; then 34 | echo Y >&3 35 | else 36 | echo N >&3 37 | fi 38 | 39 | read -s -t 1 choice || true 40 | return ${result} 41 | 42 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 43 | -------------------------------------------------------------------------------- /src/utils/user/input.xsh: -------------------------------------------------------------------------------- 1 | ## [msg [default [max_len [timeout [silent]]]]] 2 | ## Request user info. 3 | ## 4 | ## Params: 5 | ## msg: {String} Message. 6 | ## Default: "". 7 | ## default: {String} Default value. 8 | ## Default: "". 9 | ## max_len: {Integer} Max length for input. 10 | ## Default: "". 11 | ## timeout: {Integer} Timeout. 12 | ## Default: "". 13 | ## silent: {Boolean} Silent user output? 14 | ## Default: false. 15 | ## 16 | ## Output: User input result. 17 | ## Return: 0 if valid user input; 18 | ## 1 if cancel; 19 | ## 2 if empty user input and returns default value. 20 | ## 21 | ## Usage example: 22 | ## local txt="$(@user.input "Enter text:")" 23 | ## local exitCode=$? 24 | 25 | local msg="$1" 26 | local default="$2" 27 | local max_len="$3" 28 | local timeout="$4" 29 | local silent=${5:-false} 30 | 31 | local cmd='read' 32 | 33 | if [ ! -z "${max_len}" ] && @isNumber "${max_len}"; then 34 | cmd="${cmd} -n ${max_len}" 35 | fi 36 | 37 | if [ ! -z "${timeout}" ] && @isNumber "${timeout}"; then 38 | cmd="${cmd} -t ${timeout}" 39 | fi 40 | 41 | if ${silent}; then 42 | cmd="${cmd} -s" 43 | fi 44 | 45 | # Execute 46 | local i 47 | ${cmd} -p "$(@style)${BASHX_APP_PRINT_PREFIX} ${msg}$(@style system)" i >&3 48 | local r=$? 49 | local rta=0 50 | 51 | echo >&3 52 | 53 | if [ "${i}" == "${BX_KEY_ESC}" ]; then 54 | rta=1 55 | i="${default}" 56 | else 57 | # 142 == No user input 58 | if [ "${r}" == '142' ] || [ -z "${i}" ]; then 59 | # Default value 60 | rta=2 61 | i="${default}" 62 | fi 63 | fi 64 | 65 | # Result 66 | echo "${i}" 67 | return ${rta} 68 | 69 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 70 | -------------------------------------------------------------------------------- /src/utils/user/pause.xsh: -------------------------------------------------------------------------------- 1 | ## * 2 | ## Wait for user input to continue. 3 | ## 4 | ## Params: 5 | ## *: {String} Message. 6 | ## Optional. 7 | 8 | local m="$@" 9 | 10 | if ${BX_TTY}; then 11 | if [[ -z "$m" ]]; then 12 | read -s -n 1 >&3 13 | else 14 | @log 15 | read -s -n 1 -p "$(@style)${BASHX_APP_PRINT_PREFIX} ${m}$(@style system)" >&3 16 | @log 17 | fi 18 | else 19 | @log.warn 'Not at TTY! pause cancelled...' 20 | return 254 21 | fi 22 | 23 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 24 | -------------------------------------------------------------------------------- /src/utils/user/timeout.xsh: -------------------------------------------------------------------------------- 1 | ## timeout cmd [message] 2 | ## Execute command after specific time. 3 | ## 4 | ## Params: 5 | ## timeout: {Integer} Timeout for count down. 6 | ## cmd: {String} Command to execute on count down finish. 7 | ## message: {String} Message. 8 | ## Optional. Default: "Count down". 9 | ## 10 | ## Return: {Integer} Return command exit code or "255" on user cancel. 11 | 12 | local timeout=$1 13 | local cmd="$2" 14 | local message="${3:-Count down}" 15 | 16 | @isNumber "${timeout}" || @throw.invalidParam timeout 17 | [ -z "${cmd}" ] && @throw.invalidParam cmd 18 | 19 | @log 20 | 21 | local count=${timeout} 22 | local rta=0 23 | local r 24 | local i 25 | 26 | if ${BX_TTY}; then 27 | while [[ ${count} -gt 0 ]] && [[ ${rta} -eq 0 ]] ; do 28 | @log.rewrite "${message} [${count}]... Press [C] or [ESC] to cancel..." 29 | read -n 1 -s -t 1 -p '' i 30 | r=$? 31 | if [ "${i}" == 'c' ] || [ "${i}" == 'C' ] || [ "${i}" == "${BX_KEY_ESC}" ]; then 32 | rta=1 33 | else 34 | # 142 == No user input 35 | if [ "${r}" == '142' ]; then 36 | count=$((count - 1)) 37 | fi 38 | fi 39 | done 40 | 41 | @log.rewrite # Remove last line 42 | else 43 | @log.warn "Not at TTY! timeout cancelled: [${message}] {${cmd}}" 44 | return 254 45 | fi 46 | 47 | if [[ ${count} -eq 0 ]]; then 48 | @log.rewrite 49 | eval "${cmd}" 50 | return $? 51 | else 52 | # Canceled 53 | @log.rewrite ' Cancel by user!' 54 | @log 55 | return 255 56 | fi 57 | 58 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 59 | -------------------------------------------------------------------------------- /src/utils/wait/until.xsh: -------------------------------------------------------------------------------- 1 | ## command [interval [timeout [message [debug]]]] 2 | ## Wait for command until returns 0. 3 | ## 4 | ## Params: 5 | ## command: {String} Command to execute. 6 | ## interval: {Number} Run command interval in seconds. 7 | ## Optional. Default: 1. 8 | ## timeout: {Integer} Timeout. 0 to disable timeout. 9 | ## Optional. Default: 0. 10 | ## message: {String} A message is printed while waiting. "false" string to disable output. 11 | ## Optional. Default: "Waiting for {${command}}". 12 | ## retries: {Integer} Retries limit. 0 to disable retries limit. 13 | ## Optional. Default: 0. 14 | ## debug: {Boolean} Show {command} output? 15 | ## Optional. Default: false. 16 | ## 17 | ## Return: 18 | ## 0: command finish with exit code 0. 19 | ## 1: retries limit reached. 20 | ## 2: timeout reached. 21 | 22 | local command="$1" 23 | local interval=${2:-1} 24 | local timeout=${3:-0} 25 | local message="${4:-"Waiting for {${command}}"}" 26 | local debug=${5:-false} 27 | 28 | [ ! -z "${command}" ] || @throw.invalidParam command 29 | command="( set -ex ; ${command} )" 30 | 31 | local out_log="${BASHX_APP_TMP_PATH}/__bx_waituntil_out.out" 32 | local retries_count=0 33 | 34 | local working_file="${BASHX_APP_TMP_PATH}/__bx_waituntil_working.${BASH_SUBSHELL}.flag" 35 | trap "[ ! -f '${working_file}' ] || rm -f '${working_file}' 2>/dev/null" HUP INT QUIT KILL TERM EXIT 36 | touch "${working_file}" 37 | 38 | if [[ "${message}" == "false" ]]; then 39 | message='' 40 | fi 41 | 42 | if ! ${debug}; then 43 | echo "$ ${command}" >"${out_log}" 44 | echo >>"${out_log}" 45 | # command="${command} 2>>'${out_log}'" 46 | command="${command} >/dev/null 2>/dev/null 3>/dev/null" 47 | fi 48 | 49 | ( 50 | timeout_count=0 51 | p='...' 52 | 53 | while [ -f "${working_file}" ] ; do 54 | if [[ -n "${message}" ]]; then 55 | if [[ ${timeout} -gt 1 ]]; then 56 | if [[ ${timeout} -lt ${timeout_count} ]]; then 57 | # Time out! 58 | [ ! -f "${working_file}" ] || rm -f "${working_file}" 2>/dev/null 59 | @log 60 | @log.warn "Command execution timeout (${timeout_count}s)" 61 | exit 1 62 | else 63 | timeout_count=$((timeout_count+1)) 64 | fi 65 | fi 66 | 67 | @log.rewrite "${message}${p}" 68 | 69 | if [[ "${p}" == '...' ]]; then 70 | p='.' 71 | else 72 | p="${p}." 73 | fi 74 | fi 75 | 76 | sleep 1 77 | done 78 | ) & 79 | 80 | while [ -f "${working_file}" ] ; do 81 | if eval "${command}"; then 82 | # command finish with exit code 0 83 | check_fail=false 84 | [ ! -f "${working_file}" ] || rm -f "${working_file}" 2>/dev/null 85 | sleep 1 86 | return 0 87 | else 88 | if [[ ${retries} -gt 1 ]]; then 89 | if [[ ${retries} -lt ${retries_count} ]]; then 90 | # retries limit reached 91 | [ ! -f "${working_file}" ] || rm -f "${working_file}" 2>/dev/null 92 | @log 93 | @log.warn "Command execution retries limit (${retries_count} retries)" 94 | sleep 1 95 | return 1 96 | else 97 | retries_count=$((retries_count+1)) 98 | fi 99 | fi 100 | 101 | sleep ${interval} 102 | fi 103 | done 104 | 105 | # timeout reached 106 | [ ! -f "${working_file}" ] || rm -f "${working_file}" 2>/dev/null 107 | sleep 1 108 | return 2 109 | 110 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 111 | -------------------------------------------------------------------------------- /src/utils/wait/untilExists.xsh: -------------------------------------------------------------------------------- 1 | ## type path [timeout [show_message]] 2 | ## Wait for directory/file until exists. 3 | ## 4 | ## Params: 5 | ## type: {char} [d|f] d=Direcotry | f=File 6 | ## path: {String} Directory/file path. 7 | ## timeout: {Integer} Timeout. 0 to disable timeout. 8 | ## Optional. Default: 0. 9 | ## show_message: {Boolean} Show message. 10 | ## Optional. Default: true. 11 | ## 12 | ## Return: 0 if file exists, 1 if file not exists after timeout. 13 | 14 | local type="$1" 15 | local path="$2" 16 | local timeout=${3:-0} 17 | local show_message=${4:-true} 18 | 19 | local cmd 20 | local msg 21 | 22 | case "${type}" in 23 | d|D) cmd="[ -d '${path}' ]" ;; 24 | f|F) cmd="[ -f '${path}' ]" ;; 25 | *) @throw.invalidParam type 26 | fi 27 | 28 | if ! ${show_message}; then 29 | msg='false' 30 | fi 31 | 32 | @wait.until "${cmd}" 1 ${timeout} "${msg}" 33 | return $? 34 | 35 | # vim: filetype=sh tabstop=2 softtabstop=0 expandtab shiftwidth=2 smarttab 36 | --------------------------------------------------------------------------------