Posts tagged with “tutorial”

Slash Your Zsh Startup Time on macOS: Replacing NVM with FNM and Autojump with Zoxide

Okay, absolutely. Adding a cleanup step is a great idea to keep things tidy. I'll add a "Step 5" to the blog post draft.

Is opening a new terminal window or tab feeling sluggish? Do you tap your fingers waiting for your Zsh prompt to appear? If you're using popular tools like NVM (Node Version Manager) and Autojump on macOS, they might be the culprits behind that frustrating delay.

Recently, I noticed my own Zsh startup taking several seconds. After some investigation, the primary offenders were NVM's initialization script and, surprisingly, Autojump's sourcing process. The good news? There are blazing-fast, modern alternatives written in Rust: FNM (Fast Node Manager) and Zoxide.

This post will guide you through diagnosing your Zsh startup time and replacing NVM and Autojump with their speedier counterparts. Let's reclaim those lost seconds!

Step 1: Diagnose the Delay (Confirming the Culprits)

Before replacing anything, let's confirm what's slow. While Zsh's built-in zprof is useful for profiling Zsh functions, it sometimes doesn't accurately capture time spent running external commands or sourcing external scripts during startup. We'll use the simple time command:

  1. Baseline Zsh Time: How fast does Zsh start without your config?

    time zsh -f -i -c exit
    

    (-f skips .zshrc, -i simulates interactive, -c exit exits immediately). Expect this to be very fast (e.g., ~0.1 seconds).

  2. Time Potential Offenders: Check the initialization time of suspects like NVM and Autojump. (Replace paths with your actual ones).

    # Time NVM init (if you were using it) - find the source line in your .zshrc
    # Example: time zsh -f -c '[ -s "$HOME/.nvm/nvm.sh" ] && . "$HOME/.nvm/nvm.sh"'
    
    # Time Autojump init (find the source line in your .zshrc)
    # Common paths: ~/.local/share/autojump/autojump.sh or ~/Library/autojump/autojump.sh
    # Or the one often installed by Homebrew: /usr/local/etc/profile.d/autojump.sh or /opt/homebrew/etc/profile.d/autojump.sh
    time zsh -f -c '[ -f /path/to/your/autojump.sh ] && . /path/to/your/autojump.sh'
    

In my case, the Autojump sourcing took over 2 seconds, and NVM (before I removed it) was also notoriously slow. If you see significant times here (anything over a few tenths of a second adds up), replacing them is worthwhile.

Step 2: Replace NVM with FNM

FNM is significantly faster than the shell-script-based NVM.

  1. Remove NVM Config: Open your ~/.zshrc file. Find and delete or comment out the lines that load NVM. They typically look like:

    # Example lines to remove:
    # export NVM_DIR="$HOME/.nvm"
    # [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
    # [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
    # Or lines sourcing from /usr/local/opt/nvm or /opt/homebrew/opt/nvm
    
  2. Install FNM: The easiest way on macOS is via Homebrew:

    brew install fnm
    
  3. Configure FNM: Add the following line to the end of your ~/.zshrc:

    # Add this for FNM initialization
    eval "$(fnm env --use-on-cd)"
    
    • --use-on-cd: This magic flag tells FNM to automatically switch Node versions when you cd into a directory containing a .nvmrc or .node-version file.
  4. Restart Your Shell: Close and reopen your terminal, or run source ~/.zshrc.

  5. Install Node Versions: FNM manages its own installs.

    • Install latest LTS: fnm install --lts
    • Install specific version: fnm install 18
    • List installed: fnm ls
    • Set default: fnm default <version> (e.g., fnm default lts)
    • Use in current shell: fnm use <version>

Step 3: Replace Autojump with Zoxide

Zoxide is a super-fast reimplementation of autojump/z/fasd.

  1. Remove Autojump Config: Open your ~/.zshrc. Find and delete or comment out the line that sources autojump.sh. It looks like:

    # Example line to remove:
    # [ -f /path/to/your/autojump.sh ] && . /path/to/your/autojump.sh
    
  2. Install Zoxide: Again, Homebrew is simple:

    brew install zoxide
    
  3. Configure Zoxide: Add the following line to the end of your ~/.zshrc:

    # Add this for Zoxide initialization
    eval "$(zoxide init zsh)"
    
  4. Understand the Command Change: Zoxide uses z by default, not j.

    • To jump: z part_of_dir_name (e.g., z project)
  5. (Optional) Keep Using j: If you prefer j, modify the init line:

    # Use 'j' instead of 'z' for Zoxide
    eval "$(zoxide init zsh --cmd j)"
    

    Restart your shell after changing this.

  6. Import Autojump History: Don't lose your navigation history!

    • Find your autojump.txt file. Common locations are ~/.local/share/autojump/autojump.txt or (as in my case) ~/Library/autojump/autojump.txt. Use ls or find ~ -name autojump.txt to locate yours.

    • Run the import command (replace the path with your actual path):

      zoxide import --from autojump /path/to/your/autojump.txt
      

      Example: zoxide import --from autojump ~/Library/autojump/autojump.txt

  7. Restart Your Shell: Close and reopen your terminal, or run source ~/.zshrc.

Step 4: Enjoy the Speed!

Now, open a new terminal tab or window. You should feel a significant difference – that multi-second delay should be gone, replaced by a near-instantaneous prompt!

You can even run time zsh -i -c exit again (note: this won't load .zshrc, so it tests the core Zsh plus anything in global configs; simply opening a new tab is the best "feel" test) to measure the difference, though the real proof is the absence of that annoying wait.

Step 5: Clean Up Old Files (Optional but Recommended)

Once you are completely confident that FNM and Zoxide are working correctly and you've imported your data, you can remove the old files and directories to free up space and prevent potential future conflicts.

Warning: The rm -rf command permanently deletes files and directories. Double-check the paths carefully before running these commands!

  1. Remove NVM Directory: This directory contains all the Node versions NVM managed.

    # Double-check this is the correct path for your NVM installation!
    rm -rf ~/.nvm
    
  2. Remove Autojump Data Directory: This contains the old database and any other files Autojump used. Check which location yours was in (from Step 3, part 6).

    • If it was in ~/.local/share/autojump:

      rm -rf ~/.local/share/autojump
      
    • If it was in ~/Library/autojump:

      rm -rf ~/Library/autojump
      
    • (If it was somewhere else, adjust the path accordingly)

  3. (Optional) Remove via Homebrew: If you originally installed NVM or Autojump using Homebrew and don't need the Homebrew package entry anymore, you can also uninstall them via Homebrew. Note: This usually won't remove the data directories (~/.nvm, ~/Library/autojump, etc.) which is why the rm -rf commands above are still necessary for a full cleanup.

    # brew uninstall nvm
    # brew uninstall autojump
    

Performing this cleanup step isn't strictly necessary for speed, but it keeps your home directory tidy and avoids potential confusion down the line.

By swapping out older, script-based tools for their faster, compiled counterparts, you can make your Zsh experience much smoother. Happy (and faster) navigating!

Understanding `COUNT`, `LAST`, `FIRST`, and `NEXT` in PL/SQL Collections

TL;DR

In PL/SQL, COUNT, LAST, FIRST, and NEXT are essential for handling collections, especially if there are gaps (unpopulated indexes). Here’s a quick summary:

  • COUNT: Gives the number of populated entries.
  • LAST and FIRST: Identify the highest and lowest populated indexes, respectively.
  • NEXT: Finds the next populated index, skipping gaps automatically—great for sparse collections.

In PL/SQL programming, collections like nested tables, VARRAYs, and associative arrays are powerful tools for handling sets of data. However, understanding how to work with populated and unpopulated indexes can be tricky. That’s where COUNT, LAST, FIRST, and NEXT methods come into play. These methods let you manage and iterate over collections effectively, especially when dealing with gaps (unpopulated indexes). Let’s look at each one and how to use it.

1. COUNT

  • Purpose: Returns the number of populated elements in a collection.
  • Use Case: COUNT is useful when you need the exact count of entries in a collection, especially for fully populated collections without gaps.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER;
  v_numbers NumberTable := NumberTable(1, 2, 3, 4, 5); -- Fully populated
BEGIN
  DBMS_OUTPUT.PUT_LINE('COUNT: ' || v_numbers.COUNT);  -- Outputs: 5
END;

2. LAST

  • Purpose: Returns the highest populated index in the collection, whether all indexes are filled or not.
  • Use Case: When you need to find the highest valid index, such as for looping through all possible entries.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_numbers NumberTable;
BEGIN
  v_numbers(1) := 10;
  v_numbers(3) := 30; -- Gap at index 2
  v_numbers(5) := 50;

  DBMS_OUTPUT.PUT_LINE('LAST: ' || v_numbers.LAST);  -- Outputs: 5
END;

3. FIRST

  • Purpose: Returns the lowest populated index in the collection.
  • Use Case: Similar to LAST, FIRST is used when you want to start iterating from the lowest populated index.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_numbers NumberTable;
BEGIN
  v_numbers(2) := 20;
  v_numbers(4) := 40;

  DBMS_OUTPUT.PUT_LINE('FIRST: ' || v_numbers.FIRST);  -- Outputs: 2
END;

4. NEXT

  • Purpose: Given an index, NEXT returns the next highest populated index. If there are no more populated indexes after the current one, it returns NULL.
  • Use Case: NEXT is perfect for iterating only over populated elements, skipping gaps automatically.

Example:

DECLARE
  TYPE NumberTable IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_numbers NumberTable;
  v_index PLS_INTEGER;
BEGIN
  v_numbers(1) := 10;
  v_numbers(3) := 30;
  v_numbers(5) := 50;

  v_index := v_numbers.FIRST;
  WHILE v_index IS NOT NULL LOOP
    DBMS_OUTPUT.PUT_LINE('Value at index ' || v_index || ': ' || v_numbers(v_index));
    v_index := v_numbers.NEXT(v_index);
  END LOOP;
END;

Output:

Value at index 1: 10
Value at index 3: 30
Value at index 5: 50

Choosing the Right Method

  • COUNT is ideal for knowing how many elements are populated.
  • LAST and FIRST are great for determining the range of populated indexes.
  • NEXT is best when you want to loop through only populated elements, especially if there are gaps.

Key Takeaways

Using COUNT, LAST, FIRST, and NEXT effectively allows you to handle PL/SQL collections with gaps, optimize loops, and avoid errors when accessing unpopulated indexes. For sparse collections (with gaps), FIRST and NEXT are preferred over looping with 1..LAST to ensure you only interact with valid, populated indexes.

Best Practice for Gapped Collections

When working with collections that might have gaps, avoid using 1..COUNT or 1..LAST directly in loops, as they may access unpopulated indexes and cause errors. Instead:

  1. Use FIRST and NEXT to loop through only populated elements:

    • Start with v_index := v_collection.FIRST and use v_index := v_collection.NEXT(v_index) to move to the next populated index.
    • This skips gaps automatically, making it the most efficient way to handle sparse collections.
  2. Add NULL Checks if Necessary:

    • Even with FIRST and NEXT, you can add an IF condition to verify a non-null value for specific fields if you expect incomplete records.

Example:

DECLARE
  TYPE MyCollection IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
  v_collection MyCollection;
  v_index PLS_INTEGER;
BEGIN
  v_collection(1) := 10;
  v_collection(3) := 30;
  v_collection(7) := 70;

  v_index := v_collection.FIRST;
  WHILE v_index IS NOT NULL LOOP
    DBMS_OUTPUT.PUT_LINE('Value at index ' || v_index || ': ' || v_collection(v_index));
    v_index := v_collection.NEXT(v_index);
  END LOOP;
END;

This approach ensures your code only processes valid elements, skipping over unpopulated entries smoothly.

移动介质上的Linux:使用 tmpfs、关闭 atime 以延长 SD 卡或优盘的使用寿命

我有一台老旧的 Asus C100P Chromebook,Google早在几年前就停止了对它的支持。它是32位ARM CPU,因此我没法替它更换BIOS,但我可以在developer mode下启用优盘启动,这样我就能够在优盘或者sd卡运行Linux。

然而,在使用 SD 卡或其他闪存设备作为操作系统存储时,合理管理写入操作至关重要。本文将介绍如何通过使用 tmpfs 文件系统、关闭 atime 来延长设备的使用寿命。

1. 使用 tmpfs

什么是 tmpfs?

tmpfs 是一种基于内存的临时文件系统,具有以下优点:

  • 高速存储: 数据存储在内存中,读写速度极快。
  • 动态大小: 根据实际使用情况动态分配内存,不会固定占用资源。
  • 减少写入: 适合存储临时文件和日志,显著减少对闪存的写入操作。

如何配置 tmpfs

1.1 编辑 /etc/fstab 文件

打开终端并输入:

sudo nano /etc/fstab

添加以下行以创建 tmpfs 挂载点:

tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0
tmpfs /var/log tmpfs defaults,noatime,mode=0755 0 0

1.2 挂载 tmpfs

保存文件后,运行以下命令使更改生效:

sudo mount -a

1.3 验证挂载

使用以下命令确认 tmpfs 是否成功挂载:

df -h

您应该能看到类似于 /tmp/var/log 的 tmpfs 挂载点。

1.4 设置自动复制日志到 tmpfs

为了在系统启动时自动将日志复制到 tmpfs,我们需要修改 /etc/rc.local 文件:

a. 创建一个目录来存储持久化的日志:

sudo mkdir -p /var/log.hdd
sudo cp -a /var/log/* /var/log.hdd/

b. 编辑 /etc/rc.local 文件:

sudo nano /etc/rc.local

c. 在文件中添加以下内容:

#!/bin/sh -e
# 复制日志文件到 tmpfs
cp -a /var/log.hdd/* /var/log/
exit 0

d. 确保 rc.local 文件具有执行权限:

sudo chmod +x /etc/rc.local

2. 关闭 atime

atime (访问时间) 是文件系统的一个属性,每次访问文件时都会更新。关闭 atime 可以减少不必要的写入操作,从而延长 SD 卡的寿命。

如何关闭 atime

2.1 编辑 /etc/fstab 文件

打开 /etc/fstab 文件:

sudo nano /etc/fstab

2.2 修改挂载选项

找到 SD 卡对应的挂载项(通常是根分区 /),在挂载选项中添加 noatime:

UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx / ext4 defaults,noatime 0 1

2.3 重新挂载文件系统

保存文件后,使用以下命令重新挂载文件系统:

sudo mount -o remount /

请记住,虽然这些方法可以有效延长 SD 卡的使用寿命,但仍然建议定期备份重要数据,以防意外发生。毕竟备份不做,十恶不赦!

又:

mode=1777 中的 1 表示设置了"粘滞位"(sticky bit)。具体含义如下:

  1. 777 部分:

    • 7: 所有者(owner)有读、写、执行权限
    • 7: 用户组(group)有读、写、执行权限
    • 7: 其他用户(others)有读、写、执行权限
  2. 前面的 1:

    • 表示设置了粘滞位(sticky bit)

粘滞位的作用:

  • 对于目录,当设置了粘滞位时,只有文件的所有者、目录的所有者或 root 用户才能删除或重命名该目录中的文件。
  • 这通常用于像 /tmp 这样的公共目录,允许所有用户创建文件,但防止用户删除或修改其他用户的文件。

所以 mode=1777 的含义是:

  • 所有用户都可以在该目录中创建、读取和执行文件(777)
  • 但只有文件所有者和目录所有者可以删除或重命名文件(1)

这种权限设置既保证了目录的共享性,又提供了一定的安全保护,防止用户互相干扰。